All of lore.kernel.org
 help / color / mirror / Atom feed
* [libnftnl PATCH 0/7] Stabilize debug output on different endian systems
@ 2021-11-24 17:22 Phil Sutter
  2021-11-24 17:22 ` [libnftnl PATCH v2 1/7] src: add infrastructure to infer byteorder from keys Phil Sutter
                   ` (6 more replies)
  0 siblings, 7 replies; 14+ messages in thread
From: Phil Sutter @ 2021-11-24 17:22 UTC (permalink / raw)
  To: Pablo Neira Ayuso; +Cc: netfilter-devel

Patch 1 adds byteorder information to expressions, making it possible to
print expressions' data in correct byteorder.

For set elements, more effort is required. To accomplish this, patches 2
and 3 introduce new set meta data, patches 4 and 5 extend data reg
printing to make use of this extended data and patches 6 and 7 extend
the API to allow for external input of the data when printing elements.

Due to patches 6 and 7, patches 2 and 3 are not necessary for the
following series of nftables patches. Yet they are left for the sake of
completeness and the fact that libnftnl users might want to print set
elements along with their set and therefore define meta data via the
attributes introduced in patches 2 and 3.

Pablo Neira Ayuso (1):
  src: add infrastructure to infer byteorder from keys

Phil Sutter (6):
  set: Introduce NFTNL_SET_DESC_BYTEORDER
  set: Introduce NFTNL_SET_DESC_CONCAT_DATA
  data_reg: Support varying byteorder in concat data
  data_reg: Respect each value's size
  include: Introduce and publish struct nftnl_set_desc
  set: Introduce nftnl_set_elem_snprintf_desc()

 include/common.h       | 29 +++++++++++++++++++
 include/data_reg.h     | 10 ++++++-
 include/expr.h         |  2 +-
 include/expr_ops.h     |  2 ++
 include/libnftnl/set.h | 16 +++++++++++
 include/set.h          |  7 ++---
 include/set_elem.h     |  3 --
 src/expr.c             | 51 +++++++++++++++++++++++++++++++++
 src/expr/bitwise.c     | 30 ++++++++++++++------
 src/expr/byteorder.c   | 21 ++++++++++++++
 src/expr/cmp.c         | 21 +++++++++++++-
 src/expr/ct.c          | 30 ++++++++++++++++++++
 src/expr/data_reg.c    | 64 +++++++++++++++++++++++++++++++++++++-----
 src/expr/dup.c         | 14 +++++++++
 src/expr/exthdr.c      | 14 +++++++++
 src/expr/fib.c         | 18 ++++++++++++
 src/expr/fwd.c         | 14 +++++++++
 src/expr/immediate.c   | 17 +++++++++--
 src/expr/masq.c        | 16 +++++++++++
 src/expr/meta.c        | 28 ++++++++++++++++++
 src/expr/nat.c         | 22 +++++++++++++++
 src/expr/numgen.c      | 12 ++++++++
 src/expr/osf.c         | 12 ++++++++
 src/expr/payload.c     | 14 +++++++++
 src/expr/queue.c       | 12 ++++++++
 src/expr/range.c       | 11 ++++++--
 src/expr/redir.c       | 16 +++++++++++
 src/expr/rt.c          | 19 +++++++++++++
 src/expr/socket.c      | 12 ++++++++
 src/expr/tproxy.c      | 14 +++++++++
 src/expr/tunnel.c      | 12 ++++++++
 src/expr/xfrm.c        | 18 ++++++++++++
 src/libnftnl.map       |  4 +++
 src/rule.c             |  7 +++++
 src/set.c              | 21 ++++++++++++--
 src/set_elem.c         | 47 +++++++++++++++++++++----------
 36 files changed, 612 insertions(+), 48 deletions(-)

-- 
2.33.0


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

* [libnftnl PATCH v2 1/7] src: add infrastructure to infer byteorder from keys
  2021-11-24 17:22 [libnftnl PATCH 0/7] Stabilize debug output on different endian systems Phil Sutter
@ 2021-11-24 17:22 ` Phil Sutter
  2021-12-12 17:12   ` Jeremy Sowden
  2021-11-24 17:22 ` [libnftnl PATCH 2/7] set: Introduce NFTNL_SET_DESC_BYTEORDER Phil Sutter
                   ` (5 subsequent siblings)
  6 siblings, 1 reply; 14+ messages in thread
From: Phil Sutter @ 2021-11-24 17:22 UTC (permalink / raw)
  To: Pablo Neira Ayuso; +Cc: netfilter-devel

From: Pablo Neira Ayuso <pablo@netfilter.org>

This patch adds a new .byteorder callback to expressions to allow infer
the data byteorder that is placed in registers. Given that keys have a
fixed datatype, this patch tracks register operations to obtain the data
byteorder. This new infrastructure is internal and it is only used by
the nftnl_rule_snprintf() function to make it portable regardless the
endianess.

A few examples after this patch running on x86_64:

netdev
  [ meta load protocol => reg 1 ]
  [ cmp eq reg 1 0x00000008 ]
  [ immediate reg 1 0x01020304 ]
  [ payload write reg 1 => 4b @ network header + 12 csum_type 1 csum_off 10 csum_flags 0x1 ]

root@salvia:/home/pablo/devel/scm/git-netfilter/libnftnl# nft --debug=netlink add rule netdev x z ip saddr 1.2.3.4
netdev
  [ meta load protocol => reg 1 ]
  [ cmp eq reg 1 0x00000008 ]
  [ payload load 4b @ network header + 12 => reg 1 ]
  [ cmp eq reg 1 0x01020304 ]

Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
Signed-off-by: Phil Sutter <phil@nwl.cc>
---
Changes since v1:
- Fix calls to changed nftnl_data_reg_snprintf().
- Add more byteorder callbacks
- expr/xfrm: Fix SPI endianness, was recently corrected in nftables
- ct helper: Strings are always "big endian"
- expr/cmp: Boundary comparisons are always Big Endian
- expr/range: Ranges are always in network byte order
---
 include/common.h     | 29 +++++++++++++++++++++++++
 include/data_reg.h   |  4 +++-
 include/expr.h       |  2 +-
 include/expr_ops.h   |  2 ++
 src/expr.c           | 51 ++++++++++++++++++++++++++++++++++++++++++++
 src/expr/bitwise.c   | 30 +++++++++++++++++++-------
 src/expr/byteorder.c | 21 ++++++++++++++++++
 src/expr/cmp.c       | 21 +++++++++++++++++-
 src/expr/ct.c        | 30 ++++++++++++++++++++++++++
 src/expr/data_reg.c  | 18 +++++++++++-----
 src/expr/dup.c       | 14 ++++++++++++
 src/expr/exthdr.c    | 14 ++++++++++++
 src/expr/fib.c       | 18 ++++++++++++++++
 src/expr/fwd.c       | 14 ++++++++++++
 src/expr/immediate.c | 17 ++++++++++++---
 src/expr/masq.c      | 16 ++++++++++++++
 src/expr/meta.c      | 28 ++++++++++++++++++++++++
 src/expr/nat.c       | 22 +++++++++++++++++++
 src/expr/numgen.c    | 12 +++++++++++
 src/expr/osf.c       | 12 +++++++++++
 src/expr/payload.c   | 14 ++++++++++++
 src/expr/queue.c     | 12 +++++++++++
 src/expr/range.c     | 11 ++++++++--
 src/expr/redir.c     | 16 ++++++++++++++
 src/expr/rt.c        | 19 +++++++++++++++++
 src/expr/socket.c    | 12 +++++++++++
 src/expr/tproxy.c    | 14 ++++++++++++
 src/expr/tunnel.c    | 12 +++++++++++
 src/expr/xfrm.c      | 18 ++++++++++++++++
 src/rule.c           |  7 ++++++
 src/set_elem.c       |  9 +++++---
 31 files changed, 495 insertions(+), 24 deletions(-)

diff --git a/include/common.h b/include/common.h
index d05a81ad542c1..13d709b247f92 100644
--- a/include/common.h
+++ b/include/common.h
@@ -18,4 +18,33 @@ enum nftnl_parse_input {
 	NFTNL_PARSE_FILE,
 };
 
+enum nftnl_byteorder {
+	NFTNL_BYTEORDER_UNKNOWN	= 0,
+	NFTNL_BYTEORDER_HOST,
+	NFTNL_BYTEORDER_NETWORK,
+};
+
+#define NFTNL_CTX_BYTEORDER_MAX_EXPRS	16
+
+struct nftnl_byteorder_ctx {
+	struct {
+		const struct nftnl_expr	*expr;
+		enum nftnl_byteorder	byteorder;
+	} expr[NFT_REG32_15 + 1];
+	struct {
+		uint32_t		reg;
+		struct nftnl_expr	*expr;
+	} pending[NFTNL_CTX_BYTEORDER_MAX_EXPRS];
+	uint32_t			num_pending;
+};
+
+void nftnl_reg_byteorder_set(struct nftnl_byteorder_ctx *ctx, uint32_t reg,
+			     enum nftnl_byteorder byteorder);
+enum nftnl_byteorder nftnl_reg_byteorder_get(struct nftnl_byteorder_ctx *ctx,
+					     uint32_t reg);
+void nftnl_reg_byteorder_unknown(struct nftnl_byteorder_ctx *ctx, uint32_t reg,
+				 struct nftnl_expr *expr);
+void nftnl_reg_byteorder_resolve(struct nftnl_byteorder_ctx *ctx, uint32_t reg,
+				 enum nftnl_byteorder byteorder);
+
 #endif
diff --git a/include/data_reg.h b/include/data_reg.h
index 6d2dc66858bf8..3f24b6725e148 100644
--- a/include/data_reg.h
+++ b/include/data_reg.h
@@ -5,6 +5,7 @@
 #include <stdint.h>
 #include <stdbool.h>
 #include <unistd.h>
+#include "common.h"
 
 enum {
 	DATA_NONE,
@@ -31,7 +32,8 @@ union nftnl_data_reg {
 
 int nftnl_data_reg_snprintf(char *buf, size_t size,
 			    const union nftnl_data_reg *reg,
-			    uint32_t flags, int reg_type);
+			    uint32_t flags, int reg_type,
+			    enum nftnl_byteorder byteorder);
 struct nlattr;
 
 int nftnl_parse_data(union nftnl_data_reg *data, struct nlattr *attr, int *type);
diff --git a/include/expr.h b/include/expr.h
index be45e954df5b6..50959724492e7 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;
+	uint32_t		byteorder;
 	struct expr_ops		*ops;
 	uint8_t			data[];
 };
@@ -15,5 +16,4 @@ struct nlmsghdr;
 void nftnl_expr_build_payload(struct nlmsghdr *nlh, struct nftnl_expr *expr);
 struct nftnl_expr *nftnl_expr_parse(struct nlattr *attr);
 
-
 #endif
diff --git a/include/expr_ops.h b/include/expr_ops.h
index 7a6aa23f9bd1d..161babdade596 100644
--- a/include/expr_ops.h
+++ b/include/expr_ops.h
@@ -7,6 +7,7 @@
 struct nlattr;
 struct nlmsghdr;
 struct nftnl_expr;
+struct nftnl_print_ctx;
 
 struct expr_ops {
 	const char *name;
@@ -18,6 +19,7 @@ struct expr_ops {
 	const void *(*get)(const struct nftnl_expr *e, uint16_t type, uint32_t *data_len);
 	int 	(*parse)(struct nftnl_expr *e, struct nlattr *attr);
 	void	(*build)(struct nlmsghdr *nlh, const struct nftnl_expr *e);
+	void	(*byteorder)(struct nftnl_byteorder_ctx *ctx, struct nftnl_expr *e);
 	int	(*snprintf)(char *buf, size_t len, uint32_t flags, const struct nftnl_expr *e);
 };
 
diff --git a/src/expr.c b/src/expr.c
index 277bbdeeb5d02..d958bab98e925 100644
--- a/src/expr.c
+++ b/src/expr.c
@@ -301,3 +301,54 @@ 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);
 }
+
+void nftnl_reg_byteorder_set(struct nftnl_byteorder_ctx *ctx, uint32_t reg,
+			     enum nftnl_byteorder byteorder)
+{
+       if (reg > NFT_REG32_15)
+	       return;
+
+       ctx->expr[reg].byteorder = byteorder;
+}
+
+enum nftnl_byteorder nftnl_reg_byteorder_get(struct nftnl_byteorder_ctx *ctx,
+					     uint32_t reg)
+{
+       if (reg > NFT_REG32_15)
+	       return NFTNL_BYTEORDER_UNKNOWN;
+
+       return ctx->expr[reg].byteorder;
+}
+
+void nftnl_reg_byteorder_unknown(struct nftnl_byteorder_ctx *ctx, uint32_t reg,
+				 struct nftnl_expr *expr)
+{
+       int k;
+
+       if (reg > NFT_REG32_15)
+	       return;
+
+       k = ctx->num_pending++;
+       if (k >= NFTNL_CTX_BYTEORDER_MAX_EXPRS)
+	       return;
+
+       ctx->pending[k].reg = reg;
+       ctx->pending[k].expr = expr;
+}
+
+void nftnl_reg_byteorder_resolve(struct nftnl_byteorder_ctx *ctx, uint32_t reg,
+				 enum nftnl_byteorder byteorder)
+{
+       struct nftnl_expr *expr;
+       int i;
+
+       for (i = 0; i < ctx->num_pending; i++) {
+	       if (!ctx->pending[i].expr)
+		       continue;
+	       if (ctx->pending[i].reg == reg) {
+		       expr = ctx->pending[i].expr;
+		       expr->byteorder = byteorder;
+		       ctx->pending[i].expr = NULL;
+	       }
+       }
+}
diff --git a/src/expr/bitwise.c b/src/expr/bitwise.c
index d0c7827eacec9..a21dec01d286f 100644
--- a/src/expr/bitwise.c
+++ b/src/expr/bitwise.c
@@ -209,9 +209,18 @@ nftnl_expr_bitwise_parse(struct nftnl_expr *e, struct nlattr *attr)
 	return ret;
 }
 
+static void
+nftnl_expr_bitwise_byteorder(struct nftnl_byteorder_ctx *ctx, struct nftnl_expr *e)
+{
+	struct nftnl_expr_bitwise *bitwise = nftnl_expr_data(e);
+
+	e->byteorder = nftnl_reg_byteorder_get(ctx, bitwise->sreg);
+}
+
 static int
 nftnl_expr_bitwise_snprintf_bool(char *buf, size_t remain,
-				 const struct nftnl_expr_bitwise *bitwise)
+				 const struct nftnl_expr_bitwise *bitwise,
+				 enum nftnl_byteorder byteorder)
 {
 	int offset = 0, ret;
 
@@ -220,14 +229,14 @@ nftnl_expr_bitwise_snprintf_bool(char *buf, size_t remain,
 	SNPRINTF_BUFFER_SIZE(ret, remain, offset);
 
 	ret = nftnl_data_reg_snprintf(buf + offset, remain, &bitwise->mask,
-				      0, DATA_VALUE);
+				      0, DATA_VALUE, byteorder);
 	SNPRINTF_BUFFER_SIZE(ret, remain, offset);
 
 	ret = snprintf(buf + offset, remain, ") ^ ");
 	SNPRINTF_BUFFER_SIZE(ret, remain, offset);
 
 	ret = nftnl_data_reg_snprintf(buf + offset, remain, &bitwise->xor,
-				      0, DATA_VALUE);
+				      0, DATA_VALUE, byteorder);
 	SNPRINTF_BUFFER_SIZE(ret, remain, offset);
 
 	return offset;
@@ -235,7 +244,8 @@ nftnl_expr_bitwise_snprintf_bool(char *buf, size_t remain,
 
 static int
 nftnl_expr_bitwise_snprintf_shift(char *buf, size_t remain, const char *op,
-				  const struct nftnl_expr_bitwise *bitwise)
+				  const struct nftnl_expr_bitwise *bitwise,
+				  enum nftnl_byteorder byteorder)
 {	int offset = 0, ret;
 
 	ret = snprintf(buf, remain, "reg %u = ( reg %u %s ",
@@ -243,7 +253,7 @@ nftnl_expr_bitwise_snprintf_shift(char *buf, size_t remain, const char *op,
 	SNPRINTF_BUFFER_SIZE(ret, remain, offset);
 
 	ret = nftnl_data_reg_snprintf(buf + offset, remain, &bitwise->data,
-				      0, DATA_VALUE);
+				      0, DATA_VALUE, byteorder);
 	SNPRINTF_BUFFER_SIZE(ret, remain, offset);
 
 	ret = snprintf(buf + offset, remain, ") ");
@@ -261,13 +271,16 @@ nftnl_expr_bitwise_snprintf(char *buf, size_t size,
 
 	switch (bitwise->op) {
 	case NFT_BITWISE_BOOL:
-		err = nftnl_expr_bitwise_snprintf_bool(buf, size, bitwise);
+		err = nftnl_expr_bitwise_snprintf_bool(buf, size, bitwise,
+						       e->byteorder);
 		break;
 	case NFT_BITWISE_LSHIFT:
-		err = nftnl_expr_bitwise_snprintf_shift(buf, size, "<<", bitwise);
+		err = nftnl_expr_bitwise_snprintf_shift(buf, size, "<<",
+							bitwise, e->byteorder);
 		break;
 	case NFT_BITWISE_RSHIFT:
-		err = nftnl_expr_bitwise_snprintf_shift(buf, size, ">>", bitwise);
+		err = nftnl_expr_bitwise_snprintf_shift(buf, size, ">>",
+							bitwise, e->byteorder);
 		break;
 	}
 
@@ -282,5 +295,6 @@ struct expr_ops expr_ops_bitwise = {
 	.get		= nftnl_expr_bitwise_get,
 	.parse		= nftnl_expr_bitwise_parse,
 	.build		= nftnl_expr_bitwise_build,
+	.byteorder	= nftnl_expr_bitwise_byteorder,
 	.snprintf	= nftnl_expr_bitwise_snprintf,
 };
diff --git a/src/expr/byteorder.c b/src/expr/byteorder.c
index d299745fc57b4..ae67ee6ef2b35 100644
--- a/src/expr/byteorder.c
+++ b/src/expr/byteorder.c
@@ -197,6 +197,26 @@ static inline int nftnl_str2ntoh(const char *op)
 	}
 }
 
+static void nftnl_expr_byteorder_byteorder(struct nftnl_byteorder_ctx *ctx,
+					   struct nftnl_expr *e)
+{
+	struct nftnl_expr_byteorder *byteorder = nftnl_expr_data(e);
+	enum nftnl_byteorder bo;
+
+	switch (byteorder->op) {
+	case NFT_BYTEORDER_NTOH:
+		bo = NFTNL_BYTEORDER_HOST;
+		break;
+	case NFT_BYTEORDER_HTON:
+		bo = NFTNL_BYTEORDER_NETWORK;
+		break;
+	default:
+		bo = NFTNL_BYTEORDER_UNKNOWN;
+		break;
+	}
+	nftnl_reg_byteorder_set(ctx, byteorder->dreg, bo);
+}
+
 static int
 nftnl_expr_byteorder_snprintf(char *buf, size_t remain,
 			      uint32_t flags, const struct nftnl_expr *e)
@@ -220,5 +240,6 @@ struct expr_ops expr_ops_byteorder = {
 	.get		= nftnl_expr_byteorder_get,
 	.parse		= nftnl_expr_byteorder_parse,
 	.build		= nftnl_expr_byteorder_build,
+	.byteorder	= nftnl_expr_byteorder_byteorder,
 	.snprintf	= nftnl_expr_byteorder_snprintf,
 };
diff --git a/src/expr/cmp.c b/src/expr/cmp.c
index 6030693f15d86..846a112a03231 100644
--- a/src/expr/cmp.c
+++ b/src/expr/cmp.c
@@ -176,6 +176,24 @@ static inline int nftnl_str2cmp(const char *op)
 	}
 }
 
+static void
+nftnl_expr_cmp_byteorder(struct nftnl_byteorder_ctx *ctx, struct nftnl_expr *e)
+{
+	struct nftnl_expr_cmp *cmp = nftnl_expr_data(e);
+
+	switch (cmp->op) {
+	case NFT_CMP_LT:
+	case NFT_CMP_LTE:
+	case NFT_CMP_GT:
+	case NFT_CMP_GTE:
+		e->byteorder = NFTNL_BYTEORDER_NETWORK;
+		break;
+	default:
+		e->byteorder = nftnl_reg_byteorder_get(ctx, cmp->sreg);
+		break;
+	}
+}
+
 static int
 nftnl_expr_cmp_snprintf(char *buf, size_t remain,
 			uint32_t flags, const struct nftnl_expr *e)
@@ -188,7 +206,7 @@ nftnl_expr_cmp_snprintf(char *buf, size_t remain,
 	SNPRINTF_BUFFER_SIZE(ret, remain, offset);
 
 	ret = nftnl_data_reg_snprintf(buf + offset, remain, &cmp->data,
-				      0, DATA_VALUE);
+				      0, DATA_VALUE, e->byteorder);
 	SNPRINTF_BUFFER_SIZE(ret, remain, offset);
 
 	return offset;
@@ -202,5 +220,6 @@ struct expr_ops expr_ops_cmp = {
 	.get		= nftnl_expr_cmp_get,
 	.parse		= nftnl_expr_cmp_parse,
 	.build		= nftnl_expr_cmp_build,
+	.byteorder	= nftnl_expr_cmp_byteorder,
 	.snprintf	= nftnl_expr_cmp_snprintf,
 };
diff --git a/src/expr/ct.c b/src/expr/ct.c
index d5dfc81cfe0d1..fe4fc43688eb5 100644
--- a/src/expr/ct.c
+++ b/src/expr/ct.c
@@ -222,6 +222,35 @@ static inline int str2ctdir(const char *str, uint8_t *ctdir)
 	return -1;
 }
 
+static void
+nftnl_expr_ct_byteorder(struct nftnl_byteorder_ctx *ctx, struct nftnl_expr *e)
+{
+	struct nftnl_expr_ct *ct = nftnl_expr_data(e);
+
+	switch (ct->key) {
+	case NFT_CT_HELPER:
+	case NFT_CT_SRC:
+	case NFT_CT_DST:
+	case NFT_CT_PROTOCOL:
+	case NFT_CT_PROTO_SRC:
+	case NFT_CT_PROTO_DST:
+	case NFT_CT_SRC_IP:
+	case NFT_CT_DST_IP:
+	case NFT_CT_SRC_IP6:
+	case NFT_CT_DST_IP6:
+	case NFT_CT_ID:
+		e->byteorder = NFTNL_BYTEORDER_NETWORK;
+		break;
+	default:
+		e->byteorder = NFTNL_BYTEORDER_HOST;
+	}
+
+	if (e->flags & (1 << NFTNL_EXPR_CT_SREG))
+		nftnl_reg_byteorder_resolve(ctx, ct->sreg, e->byteorder);
+	if (e->flags & (1 << NFTNL_EXPR_CT_DREG))
+		nftnl_reg_byteorder_set(ctx, ct->dreg, e->byteorder);
+}
+
 static int
 nftnl_expr_ct_snprintf(char *buf, size_t remain,
 		       uint32_t flags, const struct nftnl_expr *e)
@@ -258,5 +287,6 @@ struct expr_ops expr_ops_ct = {
 	.get		= nftnl_expr_ct_get,
 	.parse		= nftnl_expr_ct_parse,
 	.build		= nftnl_expr_ct_build,
+	.byteorder	= nftnl_expr_ct_byteorder,
 	.snprintf	= nftnl_expr_ct_snprintf,
 };
diff --git a/src/expr/data_reg.c b/src/expr/data_reg.c
index 2633a775c90cc..789958ea66c0d 100644
--- a/src/expr/data_reg.c
+++ b/src/expr/data_reg.c
@@ -27,16 +27,22 @@
 static int
 nftnl_data_reg_value_snprintf_default(char *buf, size_t remain,
 				      const union nftnl_data_reg *reg,
-				      uint32_t flags)
+				      uint32_t flags,
+				      enum nftnl_byteorder byteorder)
 {
 	const char *pfx = flags & DATA_F_NOPFX ? "" : "0x";
 	int offset = 0, ret, i;
+	uint32_t value;
 
 
 
 	for (i = 0; i < div_round_up(reg->len, sizeof(uint32_t)); i++) {
-		ret = snprintf(buf + offset, remain,
-			       "%s%.8x ", pfx, reg->val[i]);
+		if (byteorder == NFTNL_BYTEORDER_NETWORK)
+			value = ntohl(reg->val[i]);
+		else
+			value = reg->val[i];
+
+		ret = snprintf(buf + offset, remain, "%s%.8x ", pfx, value);
 		SNPRINTF_BUFFER_SIZE(ret, remain, offset);
 	}
 
@@ -63,12 +69,14 @@ nftnl_data_reg_verdict_snprintf_def(char *buf, size_t size,
 
 int nftnl_data_reg_snprintf(char *buf, size_t size,
 			    const union nftnl_data_reg *reg,
-			    uint32_t flags, int reg_type)
+			    uint32_t flags, int reg_type,
+			    enum nftnl_byteorder byteorder)
 {
 	switch(reg_type) {
 	case DATA_VALUE:
 		return nftnl_data_reg_value_snprintf_default(buf, size,
-							     reg, flags);
+							     reg, flags,
+							     byteorder);
 	case DATA_VERDICT:
 	case DATA_CHAIN:
 		return nftnl_data_reg_verdict_snprintf_def(buf, size,
diff --git a/src/expr/dup.c b/src/expr/dup.c
index f041b551a7e78..58c3fe6989e91 100644
--- a/src/expr/dup.c
+++ b/src/expr/dup.c
@@ -111,6 +111,19 @@ static int nftnl_expr_dup_parse(struct nftnl_expr *e, struct nlattr *attr)
 	return ret;
 }
 
+static void
+nftnl_expr_dup_byteorder(struct nftnl_byteorder_ctx *ctx, struct nftnl_expr *e)
+{
+	struct nftnl_expr_dup *dup = nftnl_expr_data(e);
+
+	if (e->flags & (1 << NFTNL_EXPR_DUP_SREG_ADDR))
+		nftnl_reg_byteorder_resolve(ctx, dup->sreg_addr,
+					    NFTNL_BYTEORDER_NETWORK);
+	if (e->flags & (1 << NFTNL_EXPR_DUP_SREG_DEV))
+		nftnl_reg_byteorder_resolve(ctx, dup->sreg_dev,
+					    NFTNL_BYTEORDER_HOST);
+}
+
 static int nftnl_expr_dup_snprintf(char *buf, size_t remain,
 				   uint32_t flags, const struct nftnl_expr *e)
 {
@@ -138,5 +151,6 @@ struct expr_ops expr_ops_dup = {
 	.get		= nftnl_expr_dup_get,
 	.parse		= nftnl_expr_dup_parse,
 	.build		= nftnl_expr_dup_build,
+	.byteorder	= nftnl_expr_dup_byteorder,
 	.snprintf	= nftnl_expr_dup_snprintf,
 };
diff --git a/src/expr/exthdr.c b/src/expr/exthdr.c
index 1b813b1e47c4d..280fe73f48041 100644
--- a/src/expr/exthdr.c
+++ b/src/expr/exthdr.c
@@ -235,6 +235,19 @@ static inline int str2exthdr_type(const char *str)
 	return -1;
 }
 
+static void
+nftnl_expr_exthdr_byteorder(struct nftnl_byteorder_ctx *ctx, struct nftnl_expr *e)
+{
+	struct nftnl_expr_exthdr *exthdr = nftnl_expr_data(e);
+
+	e->byteorder = NFTNL_BYTEORDER_NETWORK;
+
+	if (e->flags & (1 << NFTNL_EXPR_EXTHDR_DREG))
+                nftnl_reg_byteorder_set(ctx, exthdr->dreg, e->byteorder);
+	if (e->flags & (1 << NFTNL_EXPR_EXTHDR_SREG))
+		nftnl_reg_byteorder_resolve(ctx, exthdr->sreg, e->byteorder);
+}
+
 static int
 nftnl_expr_exthdr_snprintf(char *buf, size_t len,
 			   uint32_t flags, const struct nftnl_expr *e)
@@ -262,5 +275,6 @@ struct expr_ops expr_ops_exthdr = {
 	.get		= nftnl_expr_exthdr_get,
 	.parse		= nftnl_expr_exthdr_parse,
 	.build		= nftnl_expr_exthdr_build,
+	.byteorder	= nftnl_expr_exthdr_byteorder,
 	.snprintf	= nftnl_expr_exthdr_snprintf,
 };
diff --git a/src/expr/fib.c b/src/expr/fib.c
index aaff52acabdbd..75194bff95f41 100644
--- a/src/expr/fib.c
+++ b/src/expr/fib.c
@@ -128,6 +128,23 @@ nftnl_expr_fib_parse(struct nftnl_expr *e, struct nlattr *attr)
 	return ret;
 }
 
+static void
+nftnl_expr_fib_byteorder(struct nftnl_byteorder_ctx *ctx, struct nftnl_expr *e)
+{
+	struct nftnl_expr_fib *fib = nftnl_expr_data(e);
+
+	switch (fib->result) {
+	case NFT_FIB_RESULT_OIFNAME:
+		e->byteorder = NFTNL_BYTEORDER_NETWORK;
+		break;
+	default:
+		e->byteorder = NFTNL_BYTEORDER_HOST;
+	}
+
+	if (e->flags & (1 << NFTNL_EXPR_FIB_DREG))
+                nftnl_reg_byteorder_set(ctx, fib->dreg, e->byteorder);
+}
+
 static const char *fib_type[NFT_FIB_RESULT_MAX + 1] = {
 	[NFT_FIB_RESULT_OIF] = "oif",
 	[NFT_FIB_RESULT_OIFNAME] = "oifname",
@@ -198,5 +215,6 @@ struct expr_ops expr_ops_fib = {
 	.get		= nftnl_expr_fib_get,
 	.parse		= nftnl_expr_fib_parse,
 	.build		= nftnl_expr_fib_build,
+	.byteorder	= nftnl_expr_fib_byteorder,
 	.snprintf	= nftnl_expr_fib_snprintf,
 };
diff --git a/src/expr/fwd.c b/src/expr/fwd.c
index 82e5a418bfae5..6875abfa80d19 100644
--- a/src/expr/fwd.c
+++ b/src/expr/fwd.c
@@ -125,6 +125,19 @@ static int nftnl_expr_fwd_parse(struct nftnl_expr *e, struct nlattr *attr)
 	return ret;
 }
 
+static void
+nftnl_expr_fwd_byteorder(struct nftnl_byteorder_ctx *ctx, struct nftnl_expr *e)
+{
+	struct nftnl_expr_fwd *fwd = nftnl_expr_data(e);
+
+	if (e->flags & (1 << NFTNL_EXPR_FWD_SREG_DEV))
+		nftnl_reg_byteorder_resolve(ctx, fwd->sreg_dev,
+					    NFTNL_BYTEORDER_HOST);
+	if (e->flags & (1 << NFTNL_EXPR_FWD_SREG_ADDR))
+		nftnl_reg_byteorder_resolve(ctx, fwd->sreg_addr,
+					    NFTNL_BYTEORDER_NETWORK);
+}
+
 static int nftnl_expr_fwd_snprintf(char *buf, size_t remain,
 				   uint32_t flags, const struct nftnl_expr *e)
 {
@@ -158,5 +171,6 @@ struct expr_ops expr_ops_fwd = {
 	.get		= nftnl_expr_fwd_get,
 	.parse		= nftnl_expr_fwd_parse,
 	.build		= nftnl_expr_fwd_build,
+	.byteorder	= nftnl_expr_fwd_byteorder,
 	.snprintf	= nftnl_expr_fwd_snprintf,
 };
diff --git a/src/expr/immediate.c b/src/expr/immediate.c
index 94b043c0fc8ab..fb291c7606bab 100644
--- a/src/expr/immediate.c
+++ b/src/expr/immediate.c
@@ -183,6 +183,14 @@ nftnl_expr_immediate_parse(struct nftnl_expr *e, struct nlattr *attr)
 	return ret;
 }
 
+static void
+nftnl_expr_immediate_byteorder(struct nftnl_byteorder_ctx *ctx, struct nftnl_expr *e)
+{
+	struct nftnl_expr_immediate *imm = nftnl_expr_data(e);
+
+	nftnl_reg_byteorder_unknown(ctx, imm->dreg, e);
+}
+
 static int
 nftnl_expr_immediate_snprintf(char *buf, size_t remain,
 			      uint32_t flags, const struct nftnl_expr *e)
@@ -195,17 +203,19 @@ nftnl_expr_immediate_snprintf(char *buf, size_t remain,
 
 	if (e->flags & (1 << NFTNL_EXPR_IMM_DATA)) {
 		ret = nftnl_data_reg_snprintf(buf + offset, remain, &imm->data,
-					      flags, DATA_VALUE);
+					      flags, DATA_VALUE, e->byteorder);
 		SNPRINTF_BUFFER_SIZE(ret, remain, offset);
 
 	} else if (e->flags & (1 << NFTNL_EXPR_IMM_VERDICT)) {
 		ret = nftnl_data_reg_snprintf(buf + offset, remain, &imm->data,
-					      flags, DATA_VERDICT);
+					      flags, DATA_VERDICT,
+					      NFTNL_BYTEORDER_UNKNOWN);
 		SNPRINTF_BUFFER_SIZE(ret, remain, offset);
 
 	} else if (e->flags & (1 << NFTNL_EXPR_IMM_CHAIN)) {
 		ret = nftnl_data_reg_snprintf(buf + offset, remain, &imm->data,
-					      flags, DATA_CHAIN);
+					      flags, DATA_CHAIN,
+					      NFTNL_BYTEORDER_UNKNOWN);
 		SNPRINTF_BUFFER_SIZE(ret, remain, offset);
 	}
 
@@ -229,5 +239,6 @@ struct expr_ops expr_ops_immediate = {
 	.get		= nftnl_expr_immediate_get,
 	.parse		= nftnl_expr_immediate_parse,
 	.build		= nftnl_expr_immediate_build,
+	.byteorder	= nftnl_expr_immediate_byteorder,
 	.snprintf	= nftnl_expr_immediate_snprintf,
 };
diff --git a/src/expr/masq.c b/src/expr/masq.c
index 684708c758390..a964e3ae1a938 100644
--- a/src/expr/masq.c
+++ b/src/expr/masq.c
@@ -131,6 +131,21 @@ nftnl_expr_masq_parse(struct nftnl_expr *e, struct nlattr *attr)
 	return 0;
 }
 
+static void
+nftnl_expr_masq_byteorder(struct nftnl_byteorder_ctx *ctx, struct nftnl_expr *e)
+{
+	struct nftnl_expr_masq *masq = nftnl_expr_data(e);
+
+	if (e->flags & (1 << NFTNL_EXPR_MASQ_REG_PROTO_MIN))
+		nftnl_reg_byteorder_resolve(ctx, masq->sreg_proto_min,
+					    NFTNL_BYTEORDER_NETWORK);
+	if (e->flags & (1 << NFTNL_EXPR_MASQ_REG_PROTO_MAX))
+		nftnl_reg_byteorder_resolve(ctx, masq->sreg_proto_max,
+					    NFTNL_BYTEORDER_NETWORK);
+
+	e->byteorder = NFTNL_BYTEORDER_NETWORK;
+}
+
 static int nftnl_expr_masq_snprintf(char *buf, size_t remain,
 				    uint32_t flags, const struct nftnl_expr *e)
 {
@@ -163,5 +178,6 @@ struct expr_ops expr_ops_masq = {
 	.get		= nftnl_expr_masq_get,
 	.parse		= nftnl_expr_masq_parse,
 	.build		= nftnl_expr_masq_build,
+	.byteorder	= nftnl_expr_masq_byteorder,
 	.snprintf	= nftnl_expr_masq_snprintf,
 };
diff --git a/src/expr/meta.c b/src/expr/meta.c
index 34fbb9bb63c03..deb14e5cb054d 100644
--- a/src/expr/meta.c
+++ b/src/expr/meta.c
@@ -191,6 +191,33 @@ static inline int str2meta_key(const char *str)
 	return -1;
 }
 
+static void nftnl_expr_meta_byteorder(struct nftnl_byteorder_ctx *ctx,
+				      struct nftnl_expr *e)
+{
+	struct nftnl_expr_meta *meta = nftnl_expr_data(e);
+
+
+	switch (meta->key) {
+	case NFT_META_PROTOCOL:
+	case NFT_META_IIFNAME:
+	case NFT_META_OIFNAME:
+	case NFT_META_BRI_IIFNAME:
+	case NFT_META_BRI_OIFNAME:
+	case NFT_META_PRANDOM:
+	case NFT_META_BRI_IIFVPROTO:
+	case NFT_META_SDIFNAME:
+		e->byteorder = NFTNL_BYTEORDER_NETWORK;
+		break;
+	default:
+		e->byteorder = NFTNL_BYTEORDER_HOST;
+	}
+
+	if (e->flags & (1 << NFTNL_EXPR_META_SREG))
+		nftnl_reg_byteorder_resolve(ctx, meta->sreg, e->byteorder);
+	if (e->flags & (1 << NFTNL_EXPR_META_DREG))
+		nftnl_reg_byteorder_set(ctx, meta->dreg, e->byteorder);
+}
+
 static int
 nftnl_expr_meta_snprintf(char *buf, size_t len,
 			 uint32_t flags, const struct nftnl_expr *e)
@@ -216,5 +243,6 @@ struct expr_ops expr_ops_meta = {
 	.get		= nftnl_expr_meta_get,
 	.parse		= nftnl_expr_meta_parse,
 	.build		= nftnl_expr_meta_build,
+	.byteorder	= nftnl_expr_meta_byteorder,
 	.snprintf	= nftnl_expr_meta_snprintf,
 };
diff --git a/src/expr/nat.c b/src/expr/nat.c
index 0a9cdd7f65f8f..785ceb20a92bf 100644
--- a/src/expr/nat.c
+++ b/src/expr/nat.c
@@ -220,6 +220,27 @@ static inline int nftnl_str2nat(const char *nat)
 	}
 }
 
+static void
+nftnl_expr_nat_byteorder(struct nftnl_byteorder_ctx *ctx, struct nftnl_expr *e)
+{
+	struct nftnl_expr_nat *nat = nftnl_expr_data(e);
+
+	if (e->flags & (1 << NFTNL_EXPR_NAT_REG_ADDR_MIN))
+		nftnl_reg_byteorder_resolve(ctx, nat->sreg_addr_min,
+					    NFTNL_BYTEORDER_NETWORK);
+	if (e->flags & (1 << NFTNL_EXPR_NAT_REG_ADDR_MAX))
+		nftnl_reg_byteorder_resolve(ctx, nat->sreg_addr_max,
+					    NFTNL_BYTEORDER_NETWORK);
+	if (e->flags & (1 << NFTNL_EXPR_NAT_REG_PROTO_MIN))
+		nftnl_reg_byteorder_resolve(ctx, nat->sreg_proto_min,
+					    NFTNL_BYTEORDER_NETWORK);
+	if (e->flags & (1 << NFTNL_EXPR_NAT_REG_PROTO_MAX))
+		nftnl_reg_byteorder_resolve(ctx, nat->sreg_proto_max,
+					    NFTNL_BYTEORDER_NETWORK);
+
+	e->byteorder = NFTNL_BYTEORDER_NETWORK;
+}
+
 static int
 nftnl_expr_nat_snprintf(char *buf, size_t remain,
 			uint32_t flags, const struct nftnl_expr *e)
@@ -274,5 +295,6 @@ struct expr_ops expr_ops_nat = {
 	.get		= nftnl_expr_nat_get,
 	.parse		= nftnl_expr_nat_parse,
 	.build		= nftnl_expr_nat_build,
+	.byteorder	= nftnl_expr_nat_byteorder,
 	.snprintf	= nftnl_expr_nat_snprintf,
 };
diff --git a/src/expr/numgen.c b/src/expr/numgen.c
index 159dfeca3618b..dfbeeaf1b172a 100644
--- a/src/expr/numgen.c
+++ b/src/expr/numgen.c
@@ -142,6 +142,17 @@ nftnl_expr_ng_parse(struct nftnl_expr *e, struct nlattr *attr)
 	return ret;
 }
 
+static void
+nftnl_expr_ng_byteorder(struct nftnl_byteorder_ctx *ctx, struct nftnl_expr *e)
+{
+	struct nftnl_expr_ng *ng = nftnl_expr_data(e);
+
+	e->byteorder = NFTNL_BYTEORDER_HOST;
+
+	if (e->flags & (1 << NFTNL_EXPR_NG_DREG))
+		nftnl_reg_byteorder_set(ctx, ng->dreg, e->byteorder);
+}
+
 static int
 nftnl_expr_ng_snprintf(char *buf, size_t remain,
 		       uint32_t flags, const struct nftnl_expr *e)
@@ -180,5 +191,6 @@ struct expr_ops expr_ops_ng = {
 	.get		= nftnl_expr_ng_get,
 	.parse		= nftnl_expr_ng_parse,
 	.build		= nftnl_expr_ng_build,
+	.byteorder	= nftnl_expr_ng_byteorder,
 	.snprintf	= nftnl_expr_ng_snprintf,
 };
diff --git a/src/expr/osf.c b/src/expr/osf.c
index 215a681a97aae..b2e4294877c05 100644
--- a/src/expr/osf.c
+++ b/src/expr/osf.c
@@ -124,6 +124,17 @@ nftnl_expr_osf_parse(struct nftnl_expr *e, struct nlattr *attr)
 	return 0;
 }
 
+static void
+nftnl_expr_osf_byteorder(struct nftnl_byteorder_ctx *ctx, struct nftnl_expr *e)
+{
+	struct nftnl_expr_osf *osf = nftnl_expr_data(e);
+
+	e->byteorder = NFTNL_BYTEORDER_NETWORK;
+
+	if (e->flags & (1 << NFTNL_EXPR_OSF_DREG))
+                nftnl_reg_byteorder_set(ctx, osf->dreg, e->byteorder);
+}
+
 static int
 nftnl_expr_osf_snprintf(char *buf, size_t len,
 			uint32_t flags, const struct nftnl_expr *e)
@@ -147,5 +158,6 @@ struct expr_ops expr_ops_osf = {
 	.get		= nftnl_expr_osf_get,
 	.parse		= nftnl_expr_osf_parse,
 	.build		= nftnl_expr_osf_build,
+	.byteorder	= nftnl_expr_osf_byteorder,
 	.snprintf	= nftnl_expr_osf_snprintf,
 };
diff --git a/src/expr/payload.c b/src/expr/payload.c
index 82747ec8994f7..84764e837a965 100644
--- a/src/expr/payload.c
+++ b/src/expr/payload.c
@@ -232,6 +232,19 @@ static inline int nftnl_str2base(const char *base)
 	}
 }
 
+static void
+nftnl_expr_payload_byteorder(struct nftnl_byteorder_ctx *ctx, struct nftnl_expr *e)
+{
+	struct nftnl_expr_payload *payload = nftnl_expr_data(e);
+
+	e->byteorder = NFTNL_BYTEORDER_NETWORK;
+
+	if (payload->sreg)
+		nftnl_reg_byteorder_resolve(ctx, payload->sreg, e->byteorder);
+	else
+                nftnl_reg_byteorder_set(ctx, payload->dreg, e->byteorder);
+}
+
 static int
 nftnl_expr_payload_snprintf(char *buf, size_t len,
 			    uint32_t flags, const struct nftnl_expr *e)
@@ -259,5 +272,6 @@ struct expr_ops expr_ops_payload = {
 	.get		= nftnl_expr_payload_get,
 	.parse		= nftnl_expr_payload_parse,
 	.build		= nftnl_expr_payload_build,
+	.byteorder	= nftnl_expr_payload_byteorder,
 	.snprintf	= nftnl_expr_payload_snprintf,
 };
diff --git a/src/expr/queue.c b/src/expr/queue.c
index 8f70977f7de85..1a65c8ad6484a 100644
--- a/src/expr/queue.c
+++ b/src/expr/queue.c
@@ -143,6 +143,17 @@ nftnl_expr_queue_parse(struct nftnl_expr *e, struct nlattr *attr)
 	return 0;
 }
 
+static void
+nftnl_expr_queue_byteorder(struct nftnl_byteorder_ctx *ctx, struct nftnl_expr *e)
+{
+	struct nftnl_expr_queue *queue = nftnl_expr_data(e);
+
+	e->byteorder = NFTNL_BYTEORDER_HOST;
+
+	if (e->flags & (1 << NFTNL_EXPR_QUEUE_SREG_QNUM))
+                nftnl_reg_byteorder_resolve(ctx, queue->sreg_qnum, e->byteorder);
+}
+
 static int
 nftnl_expr_queue_snprintf(char *buf, size_t remain,
 			  uint32_t flags, const struct nftnl_expr *e)
@@ -193,5 +204,6 @@ struct expr_ops expr_ops_queue = {
 	.get		= nftnl_expr_queue_get,
 	.parse		= nftnl_expr_queue_parse,
 	.build		= nftnl_expr_queue_build,
+	.byteorder	= nftnl_expr_queue_byteorder,
 	.snprintf	= nftnl_expr_queue_snprintf,
 };
diff --git a/src/expr/range.c b/src/expr/range.c
index f76843a8afd0c..ab4e2e70d8d01 100644
--- a/src/expr/range.c
+++ b/src/expr/range.c
@@ -184,6 +184,12 @@ static inline int nftnl_str2range(const char *op)
 	}
 }
 
+static void nftnl_expr_range_byteorder(struct nftnl_byteorder_ctx *ctx,
+				       struct nftnl_expr *e)
+{
+	e->byteorder = NFTNL_BYTEORDER_NETWORK;
+}
+
 static int nftnl_expr_range_snprintf(char *buf, size_t remain,
 				     uint32_t flags, const struct nftnl_expr *e)
 {
@@ -195,11 +201,11 @@ static int nftnl_expr_range_snprintf(char *buf, size_t remain,
 	SNPRINTF_BUFFER_SIZE(ret, remain, offset);
 
 	ret = nftnl_data_reg_snprintf(buf + offset, remain, &range->data_from,
-				      0, DATA_VALUE);
+				      0, DATA_VALUE, e->byteorder);
 	SNPRINTF_BUFFER_SIZE(ret, remain, offset);
 
 	ret = nftnl_data_reg_snprintf(buf + offset, remain, &range->data_to,
-				      0, DATA_VALUE);
+				      0, DATA_VALUE, e->byteorder);
 	SNPRINTF_BUFFER_SIZE(ret, remain, offset);
 
 	return offset;
@@ -213,5 +219,6 @@ struct expr_ops expr_ops_range = {
 	.get		= nftnl_expr_range_get,
 	.parse		= nftnl_expr_range_parse,
 	.build		= nftnl_expr_range_build,
+	.byteorder	= nftnl_expr_range_byteorder,
 	.snprintf	= nftnl_expr_range_snprintf,
 };
diff --git a/src/expr/redir.c b/src/expr/redir.c
index 4f56cb4302b30..3c1ebc13909e5 100644
--- a/src/expr/redir.c
+++ b/src/expr/redir.c
@@ -131,6 +131,21 @@ nftnl_expr_redir_parse(struct nftnl_expr *e, struct nlattr *attr)
 	return 0;
 }
 
+static void
+nftnl_expr_redir_byteorder(struct nftnl_byteorder_ctx *ctx, struct nftnl_expr *e)
+{
+	struct nftnl_expr_redir *redir = nftnl_expr_data(e);
+
+	if (e->flags & (1 << NFTNL_EXPR_REDIR_REG_PROTO_MIN))
+		nftnl_reg_byteorder_resolve(ctx, redir->sreg_proto_min,
+					    NFTNL_BYTEORDER_NETWORK);
+	if (e->flags & (1 << NFTNL_EXPR_REDIR_REG_PROTO_MAX))
+		nftnl_reg_byteorder_resolve(ctx, redir->sreg_proto_max,
+					    NFTNL_BYTEORDER_NETWORK);
+
+	e->byteorder = NFTNL_BYTEORDER_NETWORK;
+}
+
 static int
 nftnl_expr_redir_snprintf(char *buf, size_t remain,
 			  uint32_t flags, const struct nftnl_expr *e)
@@ -167,5 +182,6 @@ struct expr_ops expr_ops_redir = {
 	.get		= nftnl_expr_redir_get,
 	.parse		= nftnl_expr_redir_parse,
 	.build		= nftnl_expr_redir_build,
+	.byteorder	= nftnl_expr_redir_byteorder,
 	.snprintf	= nftnl_expr_redir_snprintf,
 };
diff --git a/src/expr/rt.c b/src/expr/rt.c
index 1ad9b2ad4043f..bd8b2a91948ee 100644
--- a/src/expr/rt.c
+++ b/src/expr/rt.c
@@ -112,6 +112,24 @@ nftnl_expr_rt_parse(struct nftnl_expr *e, struct nlattr *attr)
 	return 0;
 }
 
+static void nftnl_expr_rt_byteorder(struct nftnl_byteorder_ctx *ctx,
+				    struct nftnl_expr *e)
+{
+	struct nftnl_expr_rt *rt = nftnl_expr_data(e);
+
+	switch (rt->key) {
+	case NFT_RT_NEXTHOP4:
+	case NFT_RT_NEXTHOP6:
+		e->byteorder = NFTNL_BYTEORDER_NETWORK;
+		break;
+	default:
+		e->byteorder = NFTNL_BYTEORDER_HOST;
+	}
+
+	if (e->flags & (1 << NFTNL_EXPR_RT_DREG))
+		nftnl_reg_byteorder_set(ctx, rt->dreg, e->byteorder);
+}
+
 static const char *rt_key2str_array[NFT_RT_MAX + 1] = {
 	[NFT_RT_CLASSID]	= "classid",
 	[NFT_RT_NEXTHOP4]	= "nexthop4",
@@ -162,5 +180,6 @@ struct expr_ops expr_ops_rt = {
 	.get		= nftnl_expr_rt_get,
 	.parse		= nftnl_expr_rt_parse,
 	.build		= nftnl_expr_rt_build,
+	.byteorder	= nftnl_expr_rt_byteorder,
 	.snprintf	= nftnl_expr_rt_snprintf,
 };
diff --git a/src/expr/socket.c b/src/expr/socket.c
index 02d86f8ac57c0..dae9ea22bd24f 100644
--- a/src/expr/socket.c
+++ b/src/expr/socket.c
@@ -126,6 +126,17 @@ nftnl_expr_socket_parse(struct nftnl_expr *e, struct nlattr *attr)
 	return 0;
 }
 
+static void
+nftnl_expr_socket_byteorder(struct nftnl_byteorder_ctx *ctx, struct nftnl_expr *e)
+{
+	struct nftnl_expr_socket *socket = nftnl_expr_data(e);
+
+	e->byteorder = NFTNL_BYTEORDER_HOST;
+
+	if (e->flags & (1 << NFTNL_EXPR_SOCKET_DREG))
+                nftnl_reg_byteorder_set(ctx, socket->dreg, e->byteorder);
+}
+
 static const char *socket_key2str_array[NFT_SOCKET_MAX + 1] = {
 	[NFT_SOCKET_TRANSPARENT] = "transparent",
 	[NFT_SOCKET_MARK] = "mark",
@@ -165,5 +176,6 @@ struct expr_ops expr_ops_socket = {
 	.get		= nftnl_expr_socket_get,
 	.parse		= nftnl_expr_socket_parse,
 	.build		= nftnl_expr_socket_build,
+	.byteorder	= nftnl_expr_socket_byteorder,
 	.snprintf	= nftnl_expr_socket_snprintf,
 };
diff --git a/src/expr/tproxy.c b/src/expr/tproxy.c
index d3ee8f89b6db3..ef3e20569c1cf 100644
--- a/src/expr/tproxy.c
+++ b/src/expr/tproxy.c
@@ -134,6 +134,19 @@ nftnl_expr_tproxy_build(struct nlmsghdr *nlh, const struct nftnl_expr *e)
 				 htonl(tproxy->sreg_port));
 }
 
+static void nftnl_expr_tproxy_byteorder(struct nftnl_byteorder_ctx *ctx,
+				        struct nftnl_expr *e)
+{
+	struct nftnl_expr_tproxy *t = nftnl_expr_data(e);
+
+	e->byteorder = NFTNL_BYTEORDER_NETWORK;
+
+	if (e->flags & (1 << NFTNL_EXPR_TPROXY_REG_ADDR))
+		nftnl_reg_byteorder_resolve(ctx, t->sreg_addr, e->byteorder);
+	if (e->flags & (1 << NFTNL_EXPR_TPROXY_REG_PORT))
+		nftnl_reg_byteorder_resolve(ctx, t->sreg_port, e->byteorder);
+}
+
 static int
 nftnl_expr_tproxy_snprintf(char *buf, size_t remain,
 			uint32_t flags, const struct nftnl_expr *e)
@@ -170,5 +183,6 @@ struct expr_ops expr_ops_tproxy = {
 	.get		= nftnl_expr_tproxy_get,
 	.parse		= nftnl_expr_tproxy_parse,
 	.build		= nftnl_expr_tproxy_build,
+	.byteorder	= nftnl_expr_tproxy_byteorder,
 	.snprintf	= nftnl_expr_tproxy_snprintf,
 };
diff --git a/src/expr/tunnel.c b/src/expr/tunnel.c
index 1460fd26b0fbc..94a6c9bb4add2 100644
--- a/src/expr/tunnel.c
+++ b/src/expr/tunnel.c
@@ -111,6 +111,17 @@ nftnl_expr_tunnel_parse(struct nftnl_expr *e, struct nlattr *attr)
 	return 0;
 }
 
+static void
+nftnl_expr_tunnel_byteorder(struct nftnl_byteorder_ctx *ctx, struct nftnl_expr *e)
+{
+	struct nftnl_expr_tunnel *tunnel = nftnl_expr_data(e);
+
+	e->byteorder = NFTNL_BYTEORDER_HOST;
+
+	if (e->flags & (1 << NFTNL_EXPR_TUNNEL_DREG))
+                nftnl_reg_byteorder_set(ctx, tunnel->dreg, e->byteorder);
+}
+
 static const char *tunnel_key2str_array[NFT_TUNNEL_MAX + 1] = {
 	[NFT_TUNNEL_PATH]	= "path",
 	[NFT_TUNNEL_ID]		= "id",
@@ -145,5 +156,6 @@ struct expr_ops expr_ops_tunnel = {
 	.get		= nftnl_expr_tunnel_get,
 	.parse		= nftnl_expr_tunnel_parse,
 	.build		= nftnl_expr_tunnel_build,
+	.byteorder	= nftnl_expr_tunnel_byteorder,
 	.snprintf	= nftnl_expr_tunnel_snprintf,
 };
diff --git a/src/expr/xfrm.c b/src/expr/xfrm.c
index c81d14d638dcd..3134b076f042e 100644
--- a/src/expr/xfrm.c
+++ b/src/expr/xfrm.c
@@ -171,6 +171,23 @@ static const char *xfrmdir2str(uint8_t dir)
 	return xfrmdir2str_array[dir];
 }
 
+static void
+nftnl_expr_xfrm_byteorder(struct nftnl_byteorder_ctx *ctx, struct nftnl_expr *e)
+{
+	struct nftnl_expr_xfrm *x = nftnl_expr_data(e);
+
+	switch (x->key) {
+	case NFT_XFRM_KEY_REQID:
+		e->byteorder = NFTNL_BYTEORDER_HOST;
+		break;
+	default:
+		e->byteorder = NFTNL_BYTEORDER_NETWORK;
+	}
+
+	if (e->flags & (1 << NFTNL_EXPR_XFRM_DREG))
+                nftnl_reg_byteorder_set(ctx, x->dreg, e->byteorder);
+}
+
 static int
 nftnl_expr_xfrm_snprintf(char *buf, size_t remain,
 			 uint32_t flags, const struct nftnl_expr *e)
@@ -196,5 +213,6 @@ struct expr_ops expr_ops_xfrm = {
 	.get		= nftnl_expr_xfrm_get,
 	.parse		= nftnl_expr_xfrm_parse,
 	.build		= nftnl_expr_xfrm_build,
+	.byteorder	= nftnl_expr_xfrm_byteorder,
 	.snprintf	= nftnl_expr_xfrm_snprintf,
 };
diff --git a/src/rule.c b/src/rule.c
index 0bb1c2a0583c1..9f1caa6feb57e 100644
--- a/src/rule.c
+++ b/src/rule.c
@@ -549,6 +549,7 @@ static int nftnl_rule_snprintf_default(char *buf, size_t remain,
 				       const struct nftnl_rule *r,
 				       uint32_t type, uint32_t flags)
 {
+	struct nftnl_byteorder_ctx ctx = {};
 	struct nftnl_expr *expr;
 	int ret, offset = 0, i;
 	const char *sep = "";
@@ -603,6 +604,12 @@ static int nftnl_rule_snprintf_default(char *buf, size_t remain,
 	ret = snprintf(buf + offset, remain, "\n");
 	SNPRINTF_BUFFER_SIZE(ret, remain, offset);
 
+	list_for_each_entry(expr, &r->expr_list, head) {
+		if (!expr->ops->byteorder)
+			continue;
+		expr->ops->byteorder(&ctx, expr);
+	}
+
 	list_for_each_entry(expr, &r->expr_list, head) {
 		ret = snprintf(buf + offset, remain, "  [ %s ", expr->ops->name);
 		SNPRINTF_BUFFER_SIZE(ret, remain, offset);
diff --git a/src/set_elem.c b/src/set_elem.c
index 12eadce1f8e0c..9b18f4def6c47 100644
--- a/src/set_elem.c
+++ b/src/set_elem.c
@@ -708,7 +708,8 @@ int nftnl_set_elem_snprintf_default(char *buf, size_t remain,
 	SNPRINTF_BUFFER_SIZE(ret, remain, offset);
 
 	ret = nftnl_data_reg_snprintf(buf + offset, remain, &e->key,
-				      DATA_F_NOPFX, DATA_VALUE);
+				      DATA_F_NOPFX, DATA_VALUE,
+				      NFTNL_BYTEORDER_UNKNOWN);
 	SNPRINTF_BUFFER_SIZE(ret, remain, offset);
 
 	if (e->flags & (1 << NFTNL_SET_ELEM_KEY_END)) {
@@ -716,7 +717,8 @@ int nftnl_set_elem_snprintf_default(char *buf, size_t remain,
 		SNPRINTF_BUFFER_SIZE(ret, remain, offset);
 
 		ret = nftnl_data_reg_snprintf(buf + offset, remain, &e->key_end,
-					      DATA_F_NOPFX, DATA_VALUE);
+					      DATA_F_NOPFX, DATA_VALUE,
+					      NFTNL_BYTEORDER_UNKNOWN);
 		SNPRINTF_BUFFER_SIZE(ret, remain, offset);
 	}
 
@@ -727,7 +729,8 @@ int nftnl_set_elem_snprintf_default(char *buf, size_t remain,
 		dregtype = DATA_VERDICT;
 
 	ret = nftnl_data_reg_snprintf(buf + offset, remain, &e->data,
-				      DATA_F_NOPFX, dregtype);
+				      DATA_F_NOPFX, dregtype,
+				      NFTNL_BYTEORDER_UNKNOWN);
 	SNPRINTF_BUFFER_SIZE(ret, remain, offset);
 
 	ret = snprintf(buf + offset, remain, "%u [end]", e->set_elem_flags);
-- 
2.33.0


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

* [libnftnl PATCH 2/7] set: Introduce NFTNL_SET_DESC_BYTEORDER
  2021-11-24 17:22 [libnftnl PATCH 0/7] Stabilize debug output on different endian systems Phil Sutter
  2021-11-24 17:22 ` [libnftnl PATCH v2 1/7] src: add infrastructure to infer byteorder from keys Phil Sutter
@ 2021-11-24 17:22 ` Phil Sutter
  2021-11-24 17:22 ` [libnftnl PATCH 3/7] set: Introduce NFTNL_SET_DESC_CONCAT_DATA Phil Sutter
                   ` (4 subsequent siblings)
  6 siblings, 0 replies; 14+ messages in thread
From: Phil Sutter @ 2021-11-24 17:22 UTC (permalink / raw)
  To: Pablo Neira Ayuso; +Cc: netfilter-devel

This attribute allows to specify byteorder of data stored in this set's
elements.

In general, elements make use of up to three nftnl_data_reg unions: one
for the key, one for the data (with maps) and one for key_end (with
concatenated ranges). Byteorder of key and key_end is always identical.

To represent byteorder of a data_reg, 16bits are needed: each marking
whether the field at same position in 'val' array is network or host
byteorder. So for storing elements' data byteorder, a single 32bit
variable is sufficient.

Signed-off-by: Phil Sutter <phil@nwl.cc>
---
 include/libnftnl/set.h | 1 +
 include/set.h          | 1 +
 src/set.c              | 8 ++++++++
 3 files changed, 10 insertions(+)

diff --git a/include/libnftnl/set.h b/include/libnftnl/set.h
index e2e5795aa9b40..1ffb6c415260d 100644
--- a/include/libnftnl/set.h
+++ b/include/libnftnl/set.h
@@ -32,6 +32,7 @@ enum nftnl_set_attr {
 	NFTNL_SET_DESC_CONCAT,
 	NFTNL_SET_EXPR,
 	NFTNL_SET_EXPRESSIONS,
+	NFTNL_SET_DESC_BYTEORDER,
 	__NFTNL_SET_MAX
 };
 #define NFTNL_SET_MAX (__NFTNL_SET_MAX - 1)
diff --git a/include/set.h b/include/set.h
index 55018b6b9ba95..816bd24faf651 100644
--- a/include/set.h
+++ b/include/set.h
@@ -25,6 +25,7 @@ struct nftnl_set {
 	enum nft_set_policies	policy;
 	struct {
 		uint32_t	size;
+		uint32_t	byteorder;
 		uint8_t		field_len[NFT_REG32_COUNT];
 		uint8_t		field_count;
 	} desc;
diff --git a/src/set.c b/src/set.c
index c46f8277ff687..651eaf5503dee 100644
--- a/src/set.c
+++ b/src/set.c
@@ -99,6 +99,7 @@ void nftnl_set_unset(struct nftnl_set *s, uint16_t attr)
 	case NFTNL_SET_DESC_CONCAT:
 	case NFTNL_SET_TIMEOUT:
 	case NFTNL_SET_GC_INTERVAL:
+	case NFTNL_SET_DESC_BYTEORDER:
 		break;
 	case NFTNL_SET_USERDATA:
 		xfree(s->user.data);
@@ -128,6 +129,7 @@ static uint32_t nftnl_set_validate[NFTNL_SET_MAX + 1] = {
 	[NFTNL_SET_DESC_SIZE]	= sizeof(uint32_t),
 	[NFTNL_SET_TIMEOUT]		= sizeof(uint64_t),
 	[NFTNL_SET_GC_INTERVAL]	= sizeof(uint32_t),
+	[NFTNL_SET_DESC_BYTEORDER]	= sizeof(uint32_t),
 };
 
 EXPORT_SYMBOL(nftnl_set_set_data);
@@ -216,6 +218,9 @@ int nftnl_set_set_data(struct nftnl_set *s, uint16_t attr, const void *data,
 		expr = (void *)data;
 		list_add(&expr->head, &s->expr_list);
 		break;
+	case NFTNL_SET_DESC_BYTEORDER:
+		s->desc.byteorder = *(uint32_t *)data;
+		break;
 	}
 	s->flags |= (1 << attr);
 	return 0;
@@ -310,6 +315,9 @@ const void *nftnl_set_get_data(const struct nftnl_set *s, uint16_t attr,
 		list_for_each_entry(expr, &s->expr_list, head)
 			break;
 		return expr;
+	case NFTNL_SET_DESC_BYTEORDER:
+		*data_len = sizeof(uint32_t);
+		return &s->desc.byteorder;
 	}
 	return NULL;
 }
-- 
2.33.0


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

* [libnftnl PATCH 3/7] set: Introduce NFTNL_SET_DESC_CONCAT_DATA
  2021-11-24 17:22 [libnftnl PATCH 0/7] Stabilize debug output on different endian systems Phil Sutter
  2021-11-24 17:22 ` [libnftnl PATCH v2 1/7] src: add infrastructure to infer byteorder from keys Phil Sutter
  2021-11-24 17:22 ` [libnftnl PATCH 2/7] set: Introduce NFTNL_SET_DESC_BYTEORDER Phil Sutter
@ 2021-11-24 17:22 ` Phil Sutter
  2021-11-30 13:46   ` Pablo Neira Ayuso
  2021-11-24 17:22 ` [libnftnl PATCH 4/7] data_reg: Support varying byteorder in concat data Phil Sutter
                   ` (3 subsequent siblings)
  6 siblings, 1 reply; 14+ messages in thread
From: Phil Sutter @ 2021-11-24 17:22 UTC (permalink / raw)
  To: Pablo Neira Ayuso; +Cc: netfilter-devel

Analogous to NFTNL_SET_DESC_CONCAT, introduce a data structure
describing individual data lengths of elements' concatenated data
fields.

Signed-off-by: Phil Sutter <phil@nwl.cc>
---
 include/libnftnl/set.h | 1 +
 include/set.h          | 2 ++
 src/set.c              | 8 ++++++++
 3 files changed, 11 insertions(+)

diff --git a/include/libnftnl/set.h b/include/libnftnl/set.h
index 1ffb6c415260d..958bbc9065f67 100644
--- a/include/libnftnl/set.h
+++ b/include/libnftnl/set.h
@@ -33,6 +33,7 @@ enum nftnl_set_attr {
 	NFTNL_SET_EXPR,
 	NFTNL_SET_EXPRESSIONS,
 	NFTNL_SET_DESC_BYTEORDER,
+	NFTNL_SET_DESC_CONCAT_DATA,
 	__NFTNL_SET_MAX
 };
 #define NFTNL_SET_MAX (__NFTNL_SET_MAX - 1)
diff --git a/include/set.h b/include/set.h
index 816bd24faf651..a9f6225401a4e 100644
--- a/include/set.h
+++ b/include/set.h
@@ -28,6 +28,8 @@ struct nftnl_set {
 		uint32_t	byteorder;
 		uint8_t		field_len[NFT_REG32_COUNT];
 		uint8_t		field_count;
+		uint8_t		data_len[NFT_REG32_COUNT];
+		uint8_t		data_count;
 	} desc;
 	struct list_head	element_list;
 
diff --git a/src/set.c b/src/set.c
index 651eaf5503dee..e793282175eb5 100644
--- a/src/set.c
+++ b/src/set.c
@@ -100,6 +100,7 @@ void nftnl_set_unset(struct nftnl_set *s, uint16_t attr)
 	case NFTNL_SET_TIMEOUT:
 	case NFTNL_SET_GC_INTERVAL:
 	case NFTNL_SET_DESC_BYTEORDER:
+	case NFTNL_SET_DESC_CONCAT_DATA:
 		break;
 	case NFTNL_SET_USERDATA:
 		xfree(s->user.data);
@@ -221,6 +222,10 @@ int nftnl_set_set_data(struct nftnl_set *s, uint16_t attr, const void *data,
 	case NFTNL_SET_DESC_BYTEORDER:
 		s->desc.byteorder = *(uint32_t *)data;
 		break;
+	case NFTNL_SET_DESC_CONCAT_DATA:
+		memcpy(&s->desc.data_len, data, data_len);
+		while (s->desc.data_len[++s->desc.data_count]);
+		break;
 	}
 	s->flags |= (1 << attr);
 	return 0;
@@ -318,6 +323,9 @@ const void *nftnl_set_get_data(const struct nftnl_set *s, uint16_t attr,
 	case NFTNL_SET_DESC_BYTEORDER:
 		*data_len = sizeof(uint32_t);
 		return &s->desc.byteorder;
+	case NFTNL_SET_DESC_CONCAT_DATA:
+		*data_len = s->desc.data_count;
+		return s->desc.data_len;
 	}
 	return NULL;
 }
-- 
2.33.0


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

* [libnftnl PATCH 4/7] data_reg: Support varying byteorder in concat data
  2021-11-24 17:22 [libnftnl PATCH 0/7] Stabilize debug output on different endian systems Phil Sutter
                   ` (2 preceding siblings ...)
  2021-11-24 17:22 ` [libnftnl PATCH 3/7] set: Introduce NFTNL_SET_DESC_CONCAT_DATA Phil Sutter
@ 2021-11-24 17:22 ` Phil Sutter
  2021-11-24 17:22 ` [libnftnl PATCH 5/7] data_reg: Respect each value's size Phil Sutter
                   ` (2 subsequent siblings)
  6 siblings, 0 replies; 14+ messages in thread
From: Phil Sutter @ 2021-11-24 17:22 UTC (permalink / raw)
  To: Pablo Neira Ayuso; +Cc: netfilter-devel

Byteorder may be different in each field of reg->val array. Pass a 16bit
value where each bit indicates the field value at same offset is in
network byte order.

Signed-off-by: Phil Sutter <phil@nwl.cc>
---
 src/expr/data_reg.c | 9 +++++----
 1 file changed, 5 insertions(+), 4 deletions(-)

diff --git a/src/expr/data_reg.c b/src/expr/data_reg.c
index 789958ea66c0d..289443b47f9d6 100644
--- a/src/expr/data_reg.c
+++ b/src/expr/data_reg.c
@@ -27,8 +27,7 @@
 static int
 nftnl_data_reg_value_snprintf_default(char *buf, size_t remain,
 				      const union nftnl_data_reg *reg,
-				      uint32_t flags,
-				      enum nftnl_byteorder byteorder)
+				      uint32_t flags, uint16_t byteorder_bits)
 {
 	const char *pfx = flags & DATA_F_NOPFX ? "" : "0x";
 	int offset = 0, ret, i;
@@ -37,7 +36,7 @@ nftnl_data_reg_value_snprintf_default(char *buf, size_t remain,
 
 
 	for (i = 0; i < div_round_up(reg->len, sizeof(uint32_t)); i++) {
-		if (byteorder == NFTNL_BYTEORDER_NETWORK)
+		if (byteorder_bits & (1 << i))
 			value = ntohl(reg->val[i]);
 		else
 			value = reg->val[i];
@@ -72,11 +71,13 @@ int nftnl_data_reg_snprintf(char *buf, size_t size,
 			    uint32_t flags, int reg_type,
 			    enum nftnl_byteorder byteorder)
 {
+	uint16_t byteorder_bits = byteorder == NFTNL_BYTEORDER_NETWORK ? -1 : 0;
+
 	switch(reg_type) {
 	case DATA_VALUE:
 		return nftnl_data_reg_value_snprintf_default(buf, size,
 							     reg, flags,
-							     byteorder);
+							     byteorder_bits);
 	case DATA_VERDICT:
 	case DATA_CHAIN:
 		return nftnl_data_reg_verdict_snprintf_def(buf, size,
-- 
2.33.0


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

* [libnftnl PATCH 5/7] data_reg: Respect each value's size
  2021-11-24 17:22 [libnftnl PATCH 0/7] Stabilize debug output on different endian systems Phil Sutter
                   ` (3 preceding siblings ...)
  2021-11-24 17:22 ` [libnftnl PATCH 4/7] data_reg: Support varying byteorder in concat data Phil Sutter
@ 2021-11-24 17:22 ` Phil Sutter
  2021-11-24 17:22 ` [libnftnl PATCH 6/7] include: Introduce and publish struct nftnl_set_desc Phil Sutter
  2021-11-24 17:22 ` [libnftnl PATCH 7/7] set: Introduce nftnl_set_elem_snprintf_desc() Phil Sutter
  6 siblings, 0 replies; 14+ messages in thread
From: Phil Sutter @ 2021-11-24 17:22 UTC (permalink / raw)
  To: Pablo Neira Ayuso; +Cc: netfilter-devel

To consistently print reg->val fields on different architectures, length
of data in each field is relevant, because nftables simply casts u32
fields to the desired size value.

Prepare nftnl_data_reg_value_snprintf_default() to accept a u8 array
holding each rev->val field's size. This is compatible to data in struct
nftnl_set's 'desc' field.

Introduce print_data() which prints the relevant bytes of a given
reg->val field depending on data and host byte order, skipping leading
zeroes while doing so.

Signed-off-by: Phil Sutter <phil@nwl.cc>
---
 src/expr/data_reg.c | 61 +++++++++++++++++++++++++++++++++++++--------
 1 file changed, 51 insertions(+), 10 deletions(-)

diff --git a/src/expr/data_reg.c b/src/expr/data_reg.c
index 289443b47f9d6..6cbf0c4860cda 100644
--- a/src/expr/data_reg.c
+++ b/src/expr/data_reg.c
@@ -24,24 +24,64 @@
 #include <libnftnl/rule.h>
 #include "internal.h"
 
+/* return true if running on Big Endian */
+static bool host_is_be(void)
+{
+	const unsigned int i = 1;
+
+	return !*(uint8_t *)&i;
+}
+
+static int
+print_data(char *buf, size_t size, uint8_t *data, size_t len, bool nbo)
+{
+	int ret, offset = 0, j, start = 0, end = len, inc = 1;
+
+	/* on Little Endian with data in host byte order, reverse direction */
+	if (!nbo && !host_is_be()) {
+		start = len - 1;
+		end = -1;
+		inc = -1;
+	}
+
+	/* skip over leading nul bytes */
+	for (j = start; j != end - inc && !data[j]; j += inc)
+		;
+
+	/* print all remaining bytes with leading zero */
+	for (; j != end; j += inc) {
+		ret = snprintf(buf + offset, size, "%.2x", data[j]);
+		SNPRINTF_BUFFER_SIZE(ret, size, offset);
+	}
+	return offset;
+}
+
 static int
 nftnl_data_reg_value_snprintf_default(char *buf, size_t remain,
 				      const union nftnl_data_reg *reg,
-				      uint32_t flags, uint16_t byteorder_bits)
+				      uint32_t flags, uint16_t byteorder_bits,
+				      const uint8_t *lengths)
 {
-	const char *pfx = flags & DATA_F_NOPFX ? "" : "0x";
+	uint8_t lengths_fallback = reg->len;
+	const uint8_t *lenp = lengths;
 	int offset = 0, ret, i;
-	uint32_t value;
 
+	if (!lengths || !lengths[0])
+		lenp = &lengths_fallback;
 
+	for (i = 0; i * 4 < reg->len; i += div_round_up(*lenp++, 4)) {
 
-	for (i = 0; i < div_round_up(reg->len, sizeof(uint32_t)); i++) {
-		if (byteorder_bits & (1 << i))
-			value = ntohl(reg->val[i]);
-		else
-			value = reg->val[i];
+		if (!(flags & DATA_F_NOPFX)) {
+			ret = snprintf(buf + offset, remain, "0x");
+			SNPRINTF_BUFFER_SIZE(ret, remain, offset);
+		}
+
+		ret = print_data(buf + offset, remain,
+				 (uint8_t *)(reg->val + i),
+				 *lenp, byteorder_bits & (1 << i));
+		SNPRINTF_BUFFER_SIZE(ret, remain, offset);
 
-		ret = snprintf(buf + offset, remain, "%s%.8x ", pfx, value);
+		ret = snprintf(buf + offset, remain, " ");
 		SNPRINTF_BUFFER_SIZE(ret, remain, offset);
 	}
 
@@ -77,7 +117,8 @@ int nftnl_data_reg_snprintf(char *buf, size_t size,
 	case DATA_VALUE:
 		return nftnl_data_reg_value_snprintf_default(buf, size,
 							     reg, flags,
-							     byteorder_bits);
+							     byteorder_bits,
+							     NULL);
 	case DATA_VERDICT:
 	case DATA_CHAIN:
 		return nftnl_data_reg_verdict_snprintf_def(buf, size,
-- 
2.33.0


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

* [libnftnl PATCH 6/7] include: Introduce and publish struct nftnl_set_desc
  2021-11-24 17:22 [libnftnl PATCH 0/7] Stabilize debug output on different endian systems Phil Sutter
                   ` (4 preceding siblings ...)
  2021-11-24 17:22 ` [libnftnl PATCH 5/7] data_reg: Respect each value's size Phil Sutter
@ 2021-11-24 17:22 ` Phil Sutter
  2021-11-24 17:22 ` [libnftnl PATCH 7/7] set: Introduce nftnl_set_elem_snprintf_desc() Phil Sutter
  6 siblings, 0 replies; 14+ messages in thread
From: Phil Sutter @ 2021-11-24 17:22 UTC (permalink / raw)
  To: Pablo Neira Ayuso; +Cc: netfilter-devel

This embedded struct in struct nftnl_set holds useful info about data in
elements. Export it to users for later use as parameter to API
functions.

Signed-off-by: Phil Sutter <phil@nwl.cc>
---
 include/libnftnl/set.h | 10 ++++++++++
 include/set.h          | 10 ++--------
 2 files changed, 12 insertions(+), 8 deletions(-)

diff --git a/include/libnftnl/set.h b/include/libnftnl/set.h
index 958bbc9065f67..d19635716b581 100644
--- a/include/libnftnl/set.h
+++ b/include/libnftnl/set.h
@@ -7,6 +7,7 @@
 #include <sys/types.h>
 
 #include <libnftnl/common.h>
+#include <linux/netfilter/nf_tables.h>
 
 #ifdef __cplusplus
 extern "C" {
@@ -38,6 +39,15 @@ enum nftnl_set_attr {
 };
 #define NFTNL_SET_MAX (__NFTNL_SET_MAX - 1)
 
+struct nftnl_set_desc {
+	uint32_t	size;
+	uint32_t	byteorder;
+	uint8_t		field_len[NFT_REG32_COUNT];
+	uint8_t		field_count;
+	uint8_t		data_len[NFT_REG32_COUNT];
+	uint8_t		data_count;
+};
+
 struct nftnl_set;
 
 struct nftnl_set *nftnl_set_alloc(void);
diff --git a/include/set.h b/include/set.h
index a9f6225401a4e..42e761a2a22bf 100644
--- a/include/set.h
+++ b/include/set.h
@@ -2,6 +2,7 @@
 #define _LIBNFTNL_SET_INTERNAL_H_
 
 #include <linux/netfilter/nf_tables.h>
+#include <libnftnl/set.h>
 
 struct nftnl_set {
 	struct list_head	head;
@@ -23,14 +24,7 @@ struct nftnl_set {
 	} user;
 	uint32_t		id;
 	enum nft_set_policies	policy;
-	struct {
-		uint32_t	size;
-		uint32_t	byteorder;
-		uint8_t		field_len[NFT_REG32_COUNT];
-		uint8_t		field_count;
-		uint8_t		data_len[NFT_REG32_COUNT];
-		uint8_t		data_count;
-	} desc;
+	struct nftnl_set_desc	desc;
 	struct list_head	element_list;
 
 	uint32_t		flags;
-- 
2.33.0


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

* [libnftnl PATCH 7/7] set: Introduce nftnl_set_elem_snprintf_desc()
  2021-11-24 17:22 [libnftnl PATCH 0/7] Stabilize debug output on different endian systems Phil Sutter
                   ` (5 preceding siblings ...)
  2021-11-24 17:22 ` [libnftnl PATCH 6/7] include: Introduce and publish struct nftnl_set_desc Phil Sutter
@ 2021-11-24 17:22 ` Phil Sutter
  6 siblings, 0 replies; 14+ messages in thread
From: Phil Sutter @ 2021-11-24 17:22 UTC (permalink / raw)
  To: Pablo Neira Ayuso; +Cc: netfilter-devel

This new API function allows to print set elements' data properly
formatted according to field size and byteorder by accepting a struct
nftnl_set_desc pointer.

Pass the desc pointer internally along by extending
nftnl_set_elem_snprintf_default() signature and introduce a small helper
to call the right data reg printing routine depending on data reg type.

Since the new function is very similar to nftnl_set_elem_cmd_snprintf(),
make it replace the latter.

Signed-off-by: Phil Sutter <phil@nwl.cc>
---
 include/data_reg.h     |  6 +++++
 include/libnftnl/set.h |  4 ++++
 include/set_elem.h     |  3 ---
 src/expr/data_reg.c    |  2 +-
 src/libnftnl.map       |  4 ++++
 src/set.c              |  5 +++--
 src/set_elem.c         | 50 +++++++++++++++++++++++++++---------------
 7 files changed, 50 insertions(+), 24 deletions(-)

diff --git a/include/data_reg.h b/include/data_reg.h
index 3f24b6725e148..c9ab318266113 100644
--- a/include/data_reg.h
+++ b/include/data_reg.h
@@ -30,6 +30,12 @@ union nftnl_data_reg {
 	};
 };
 
+int
+nftnl_data_reg_value_snprintf_default(char *buf, size_t remain,
+				      const union nftnl_data_reg *reg,
+				      uint32_t flags, uint16_t byteorder_bits,
+				      const uint8_t *lengths);
+
 int nftnl_data_reg_snprintf(char *buf, size_t size,
 			    const union nftnl_data_reg *reg,
 			    uint32_t flags, int reg_type,
diff --git a/include/libnftnl/set.h b/include/libnftnl/set.h
index d19635716b581..9d9891c516624 100644
--- a/include/libnftnl/set.h
+++ b/include/libnftnl/set.h
@@ -163,6 +163,10 @@ int nftnl_set_elem_parse(struct nftnl_set_elem *e, enum nftnl_parse_type type,
 		       const char *data, struct nftnl_parse_err *err);
 int nftnl_set_elem_parse_file(struct nftnl_set_elem *e, enum nftnl_parse_type type,
 			    FILE *fp, struct nftnl_parse_err *err);
+int nftnl_set_elem_snprintf_desc(char *buf, size_t size,
+				 const struct nftnl_set_elem *e,
+				 const struct nftnl_set_desc *desc,
+				 uint32_t type, uint32_t flags);
 int nftnl_set_elem_snprintf(char *buf, size_t size, const struct nftnl_set_elem *s, uint32_t type, uint32_t flags);
 int nftnl_set_elem_fprintf(FILE *fp, const struct nftnl_set_elem *se, uint32_t type, uint32_t flags);
 
diff --git a/include/set_elem.h b/include/set_elem.h
index 76280051bb803..9239557469feb 100644
--- a/include/set_elem.h
+++ b/include/set_elem.h
@@ -20,7 +20,4 @@ struct nftnl_set_elem {
 	} user;
 };
 
-int nftnl_set_elem_snprintf_default(char *buf, size_t size,
-				    const struct nftnl_set_elem *e);
-
 #endif
diff --git a/src/expr/data_reg.c b/src/expr/data_reg.c
index 6cbf0c4860cda..c85c9e5b3dbe2 100644
--- a/src/expr/data_reg.c
+++ b/src/expr/data_reg.c
@@ -56,7 +56,7 @@ print_data(char *buf, size_t size, uint8_t *data, size_t len, bool nbo)
 	return offset;
 }
 
-static int
+int
 nftnl_data_reg_value_snprintf_default(char *buf, size_t remain,
 				      const union nftnl_data_reg *reg,
 				      uint32_t flags, uint16_t byteorder_bits,
diff --git a/src/libnftnl.map b/src/libnftnl.map
index ad8f2af060aef..595e424bd5073 100644
--- a/src/libnftnl.map
+++ b/src/libnftnl.map
@@ -387,3 +387,7 @@ LIBNFTNL_16 {
 LIBNFTNL_17 {
   nftnl_set_elem_nlmsg_build;
 } LIBNFTNL_16;
+
+LIBNFTNL_18 {
+  nftnl_set_elem_snprintf_desc;
+} LIBNFTNL_17;
diff --git a/src/set.c b/src/set.c
index e793282175eb5..2d2f45e5f129b 100644
--- a/src/set.c
+++ b/src/set.c
@@ -844,8 +844,9 @@ static int nftnl_set_snprintf_default(char *buf, size_t remain,
 		ret = snprintf(buf + offset, remain, "\t");
 		SNPRINTF_BUFFER_SIZE(ret, remain, offset);
 
-		ret = nftnl_set_elem_snprintf_default(buf + offset, remain,
-						      elem);
+		ret = nftnl_set_elem_snprintf_desc(buf + offset, remain,
+						   elem, &s->desc,
+						   NFTNL_OUTPUT_DEFAULT, 0);
 		SNPRINTF_BUFFER_SIZE(ret, remain, offset);
 	}
 
diff --git a/src/set_elem.c b/src/set_elem.c
index 9b18f4def6c47..f911f5b16a102 100644
--- a/src/set_elem.c
+++ b/src/set_elem.c
@@ -699,26 +699,39 @@ int nftnl_set_elem_parse_file(struct nftnl_set_elem *e, enum nftnl_parse_type ty
 	return -1;
 }
 
-int nftnl_set_elem_snprintf_default(char *buf, size_t remain,
-				    const struct nftnl_set_elem *e)
+static int print_dreg(char *buf, size_t size, const union nftnl_data_reg *reg,
+		      int dregtype, uint16_t byteorder, const uint8_t *lengths)
+{
+	if (dregtype == DATA_VERDICT)
+		return nftnl_data_reg_snprintf(buf, size, reg,
+					       DATA_F_NOPFX, dregtype,
+					       NFTNL_BYTEORDER_UNKNOWN);
+
+	return nftnl_data_reg_value_snprintf_default(buf, size, reg,
+						     DATA_F_NOPFX,
+						     byteorder, lengths);
+}
+
+static int
+nftnl_set_elem_snprintf_default(char *buf, size_t remain,
+				    const struct nftnl_set_elem *e,
+				    const struct nftnl_set_desc *desc)
 {
 	int ret, dregtype = DATA_VALUE, offset = 0, i;
 
 	ret = snprintf(buf, remain, "element ");
 	SNPRINTF_BUFFER_SIZE(ret, remain, offset);
 
-	ret = nftnl_data_reg_snprintf(buf + offset, remain, &e->key,
-				      DATA_F_NOPFX, DATA_VALUE,
-				      NFTNL_BYTEORDER_UNKNOWN);
+	ret = print_dreg(buf + offset, remain, &e->key, dregtype,
+			 desc->byteorder & 0xffff, desc->field_len);
 	SNPRINTF_BUFFER_SIZE(ret, remain, offset);
 
 	if (e->flags & (1 << NFTNL_SET_ELEM_KEY_END)) {
 		ret = snprintf(buf + offset, remain, " - ");
 		SNPRINTF_BUFFER_SIZE(ret, remain, offset);
 
-		ret = nftnl_data_reg_snprintf(buf + offset, remain, &e->key_end,
-					      DATA_F_NOPFX, DATA_VALUE,
-					      NFTNL_BYTEORDER_UNKNOWN);
+		ret = print_dreg(buf + offset, remain, &e->key_end, dregtype,
+				 desc->byteorder & 0xffff, desc->field_len);
 		SNPRINTF_BUFFER_SIZE(ret, remain, offset);
 	}
 
@@ -728,9 +741,8 @@ int nftnl_set_elem_snprintf_default(char *buf, size_t remain,
 	if (e->flags & (1 << NFTNL_SET_ELEM_VERDICT))
 		dregtype = DATA_VERDICT;
 
-	ret = nftnl_data_reg_snprintf(buf + offset, remain, &e->data,
-				      DATA_F_NOPFX, dregtype,
-				      NFTNL_BYTEORDER_UNKNOWN);
+	ret = print_dreg(buf + offset, remain, &e->data, dregtype,
+			 desc->byteorder >> 16, desc->data_len);
 	SNPRINTF_BUFFER_SIZE(ret, remain, offset);
 
 	ret = snprintf(buf + offset, remain, "%u [end]", e->set_elem_flags);
@@ -755,17 +767,18 @@ int nftnl_set_elem_snprintf_default(char *buf, size_t remain,
 	return offset;
 }
 
-static int nftnl_set_elem_cmd_snprintf(char *buf, size_t remain,
-				       const struct nftnl_set_elem *e,
-				       uint32_t cmd, uint32_t type,
-				       uint32_t flags)
+EXPORT_SYMBOL(nftnl_set_elem_snprintf_desc);
+int nftnl_set_elem_snprintf_desc(char *buf, size_t remain,
+				 const struct nftnl_set_elem *e,
+				 const struct nftnl_set_desc *desc,
+				 uint32_t type, uint32_t flags)
 {
 	int ret, offset = 0;
 
 	if (type != NFTNL_OUTPUT_DEFAULT)
 		return -1;
 
-	ret = nftnl_set_elem_snprintf_default(buf + offset, remain, e);
+	ret = nftnl_set_elem_snprintf_default(buf + offset, remain, e, desc);
 	SNPRINTF_BUFFER_SIZE(ret, remain, offset);
 
 	return offset;
@@ -776,11 +789,12 @@ int nftnl_set_elem_snprintf(char *buf, size_t size,
 			    const struct nftnl_set_elem *e,
 			    uint32_t type, uint32_t flags)
 {
+	struct nftnl_set_desc desc = {};
+
 	if (size)
 		buf[0] = '\0';
 
-	return nftnl_set_elem_cmd_snprintf(buf, size, e, nftnl_flag2cmd(flags),
-					 type, flags);
+	return nftnl_set_elem_snprintf_desc(buf, size, e, &desc, type, flags);
 }
 
 static int nftnl_set_elem_do_snprintf(char *buf, size_t size, const void *e,
-- 
2.33.0


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

* Re: [libnftnl PATCH 3/7] set: Introduce NFTNL_SET_DESC_CONCAT_DATA
  2021-11-24 17:22 ` [libnftnl PATCH 3/7] set: Introduce NFTNL_SET_DESC_CONCAT_DATA Phil Sutter
@ 2021-11-30 13:46   ` Pablo Neira Ayuso
  2021-11-30 17:45     ` Phil Sutter
  0 siblings, 1 reply; 14+ messages in thread
From: Pablo Neira Ayuso @ 2021-11-30 13:46 UTC (permalink / raw)
  To: Phil Sutter; +Cc: netfilter-devel

Hi Phil,

On Wed, Nov 24, 2021 at 06:22:38PM +0100, Phil Sutter wrote:
> Analogous to NFTNL_SET_DESC_CONCAT, introduce a data structure
> describing individual data lengths of elements' concatenated data
> fields.
> 
> Signed-off-by: Phil Sutter <phil@nwl.cc>
> ---
>  include/libnftnl/set.h | 1 +
>  include/set.h          | 2 ++
>  src/set.c              | 8 ++++++++
>  3 files changed, 11 insertions(+)
> 
> diff --git a/include/libnftnl/set.h b/include/libnftnl/set.h
> index 1ffb6c415260d..958bbc9065f67 100644
> --- a/include/libnftnl/set.h
> +++ b/include/libnftnl/set.h
> @@ -33,6 +33,7 @@ enum nftnl_set_attr {
>  	NFTNL_SET_EXPR,
>  	NFTNL_SET_EXPRESSIONS,
>  	NFTNL_SET_DESC_BYTEORDER,
> +	NFTNL_SET_DESC_CONCAT_DATA,

This information is already encoded in NFTNL_SET_DATA_TYPE, the
datatypes that are defined in libnftables have an explicit byteorder
and length.

For concatenation, this information is stored in 6 bits (see
TYPE_BITS). By parsing the NFTNL_SET_DATA_TYPE field you can extract
both types (and byteorders) of the set definition.

For the typeof case, where a generic datatype such as integer is used,
this information is stored in the SET_USERDATA area.

This update for libnftnl is adding a third way to describe the
datatypes in the set, right?

>  	__NFTNL_SET_MAX
>  };
>  #define NFTNL_SET_MAX (__NFTNL_SET_MAX - 1)

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

* Re: [libnftnl PATCH 3/7] set: Introduce NFTNL_SET_DESC_CONCAT_DATA
  2021-11-30 13:46   ` Pablo Neira Ayuso
@ 2021-11-30 17:45     ` Phil Sutter
  2022-01-05 16:17       ` Pablo Neira Ayuso
  0 siblings, 1 reply; 14+ messages in thread
From: Phil Sutter @ 2021-11-30 17:45 UTC (permalink / raw)
  To: Pablo Neira Ayuso; +Cc: netfilter-devel

Hi Pablo,

On Tue, Nov 30, 2021 at 02:46:58PM +0100, Pablo Neira Ayuso wrote:
> On Wed, Nov 24, 2021 at 06:22:38PM +0100, Phil Sutter wrote:
> > Analogous to NFTNL_SET_DESC_CONCAT, introduce a data structure
> > describing individual data lengths of elements' concatenated data
> > fields.
> > 
> > Signed-off-by: Phil Sutter <phil@nwl.cc>
> > ---
> >  include/libnftnl/set.h | 1 +
> >  include/set.h          | 2 ++
> >  src/set.c              | 8 ++++++++
> >  3 files changed, 11 insertions(+)
> > 
> > diff --git a/include/libnftnl/set.h b/include/libnftnl/set.h
> > index 1ffb6c415260d..958bbc9065f67 100644
> > --- a/include/libnftnl/set.h
> > +++ b/include/libnftnl/set.h
> > @@ -33,6 +33,7 @@ enum nftnl_set_attr {
> >  	NFTNL_SET_EXPR,
> >  	NFTNL_SET_EXPRESSIONS,
> >  	NFTNL_SET_DESC_BYTEORDER,
> > +	NFTNL_SET_DESC_CONCAT_DATA,
> 
> This information is already encoded in NFTNL_SET_DATA_TYPE, the
> datatypes that are defined in libnftables have an explicit byteorder
> and length.

We don't define data types in libnftnl, merely expressions and (with
your patch) those define what byteorder the source/destination registers
are supposed to be.

> For concatenation, this information is stored in 6 bits (see
> TYPE_BITS). By parsing the NFTNL_SET_DATA_TYPE field you can extract
> both types (and byteorders) of the set definition.

For this to work, I would have to duplicate nftables' enum datatypes and
in addition to that add an array defining each type's byteorder. I had
considered this once, but didn't like the amount of duplication.

> For the typeof case, where a generic datatype such as integer is used,
> this information is stored in the SET_USERDATA area.

This does not work for concatenated elements, right? At least I see e.g.
NFTNL_UDATA_SET_KEYBYTEORDER being set to set->key->byteorder, so that's
just a single value, no?

> This update for libnftnl is adding a third way to describe the
> datatypes in the set, right?

Well, it extends the logic around NFTNL_SET_DESC_CONCAT to non-interval
sets and to maps (adding the same data for the target part).

Then there is the new NFTNL_SET_DESC_BYTEORDER which defines the
byteorder of each part of the key (and value in maps).

Cheers, Phil

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

* Re: [libnftnl PATCH v2 1/7] src: add infrastructure to infer byteorder from keys
  2021-11-24 17:22 ` [libnftnl PATCH v2 1/7] src: add infrastructure to infer byteorder from keys Phil Sutter
@ 2021-12-12 17:12   ` Jeremy Sowden
  0 siblings, 0 replies; 14+ messages in thread
From: Jeremy Sowden @ 2021-12-12 17:12 UTC (permalink / raw)
  To: Phil Sutter; +Cc: Pablo Neira Ayuso, netfilter-devel

[-- Attachment #1: Type: text/plain, Size: 41347 bytes --]

On 2021-11-24, at 18:22:36 +0100, Phil Sutter wrote:
> From: Pablo Neira Ayuso <pablo@netfilter.org>
>
> This patch adds a new .byteorder callback to expressions to allow infer
> the data byteorder that is placed in registers. Given that keys have a
> fixed datatype, this patch tracks register operations to obtain the data
> byteorder. This new infrastructure is internal and it is only used by
> the nftnl_rule_snprintf() function to make it portable regardless the
> endianess.
>
> A few examples after this patch running on x86_64:
>
> netdev
>   [ meta load protocol => reg 1 ]
>   [ cmp eq reg 1 0x00000008 ]
>   [ immediate reg 1 0x01020304 ]
>   [ payload write reg 1 => 4b @ network header + 12 csum_type 1 csum_off 10 csum_flags 0x1 ]
>
> root@salvia:/home/pablo/devel/scm/git-netfilter/libnftnl# nft --debug=netlink add rule netdev x z ip saddr 1.2.3.4
> netdev
>   [ meta load protocol => reg 1 ]
>   [ cmp eq reg 1 0x00000008 ]
>   [ payload load 4b @ network header + 12 => reg 1 ]
>   [ cmp eq reg 1 0x01020304 ]
>
> Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
> Signed-off-by: Phil Sutter <phil@nwl.cc>
> ---
> Changes since v1:
> - Fix calls to changed nftnl_data_reg_snprintf().
> - Add more byteorder callbacks
> - expr/xfrm: Fix SPI endianness, was recently corrected in nftables
> - ct helper: Strings are always "big endian"
> - expr/cmp: Boundary comparisons are always Big Endian
> - expr/range: Ranges are always in network byte order
> ---
>  include/common.h     | 29 +++++++++++++++++++++++++
>  include/data_reg.h   |  4 +++-
>  include/expr.h       |  2 +-
>  include/expr_ops.h   |  2 ++
>  src/expr.c           | 51 ++++++++++++++++++++++++++++++++++++++++++++
>  src/expr/bitwise.c   | 30 +++++++++++++++++++-------
>  src/expr/byteorder.c | 21 ++++++++++++++++++
>  src/expr/cmp.c       | 21 +++++++++++++++++-
>  src/expr/ct.c        | 30 ++++++++++++++++++++++++++
>  src/expr/data_reg.c  | 18 +++++++++++-----
>  src/expr/dup.c       | 14 ++++++++++++
>  src/expr/exthdr.c    | 14 ++++++++++++
>  src/expr/fib.c       | 18 ++++++++++++++++
>  src/expr/fwd.c       | 14 ++++++++++++
>  src/expr/immediate.c | 17 ++++++++++++---
>  src/expr/masq.c      | 16 ++++++++++++++
>  src/expr/meta.c      | 28 ++++++++++++++++++++++++
>  src/expr/nat.c       | 22 +++++++++++++++++++
>  src/expr/numgen.c    | 12 +++++++++++
>  src/expr/osf.c       | 12 +++++++++++
>  src/expr/payload.c   | 14 ++++++++++++
>  src/expr/queue.c     | 12 +++++++++++
>  src/expr/range.c     | 11 ++++++++--
>  src/expr/redir.c     | 16 ++++++++++++++
>  src/expr/rt.c        | 19 +++++++++++++++++
>  src/expr/socket.c    | 12 +++++++++++
>  src/expr/tproxy.c    | 14 ++++++++++++
>  src/expr/tunnel.c    | 12 +++++++++++
>  src/expr/xfrm.c      | 18 ++++++++++++++++
>  src/rule.c           |  7 ++++++
>  src/set_elem.c       |  9 +++++---
>  31 files changed, 495 insertions(+), 24 deletions(-)
>
> diff --git a/include/common.h b/include/common.h
> index d05a81ad542c1..13d709b247f92 100644
> --- a/include/common.h
> +++ b/include/common.h
> @@ -18,4 +18,33 @@ enum nftnl_parse_input {
>  	NFTNL_PARSE_FILE,
>  };
>
> +enum nftnl_byteorder {
> +	NFTNL_BYTEORDER_UNKNOWN	= 0,
> +	NFTNL_BYTEORDER_HOST,
> +	NFTNL_BYTEORDER_NETWORK,
> +};
> +
> +#define NFTNL_CTX_BYTEORDER_MAX_EXPRS	16
> +
> +struct nftnl_byteorder_ctx {
> +	struct {
> +		const struct nftnl_expr	*expr;
> +		enum nftnl_byteorder	byteorder;
> +	} expr[NFT_REG32_15 + 1];
> +	struct {
> +		uint32_t		reg;
> +		struct nftnl_expr	*expr;
> +	} pending[NFTNL_CTX_BYTEORDER_MAX_EXPRS];
> +	uint32_t			num_pending;
> +};
> +
> +void nftnl_reg_byteorder_set(struct nftnl_byteorder_ctx *ctx, uint32_t reg,
> +			     enum nftnl_byteorder byteorder);
> +enum nftnl_byteorder nftnl_reg_byteorder_get(struct nftnl_byteorder_ctx *ctx,
> +					     uint32_t reg);
> +void nftnl_reg_byteorder_unknown(struct nftnl_byteorder_ctx *ctx, uint32_t reg,
> +				 struct nftnl_expr *expr);
> +void nftnl_reg_byteorder_resolve(struct nftnl_byteorder_ctx *ctx, uint32_t reg,
> +				 enum nftnl_byteorder byteorder);
> +
>  #endif
> diff --git a/include/data_reg.h b/include/data_reg.h
> index 6d2dc66858bf8..3f24b6725e148 100644
> --- a/include/data_reg.h
> +++ b/include/data_reg.h
> @@ -5,6 +5,7 @@
>  #include <stdint.h>
>  #include <stdbool.h>
>  #include <unistd.h>
> +#include "common.h"
>
>  enum {
>  	DATA_NONE,
> @@ -31,7 +32,8 @@ union nftnl_data_reg {
>
>  int nftnl_data_reg_snprintf(char *buf, size_t size,
>  			    const union nftnl_data_reg *reg,
> -			    uint32_t flags, int reg_type);
> +			    uint32_t flags, int reg_type,
> +			    enum nftnl_byteorder byteorder);
>  struct nlattr;
>
>  int nftnl_parse_data(union nftnl_data_reg *data, struct nlattr *attr, int *type);
> diff --git a/include/expr.h b/include/expr.h
> index be45e954df5b6..50959724492e7 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;
> +	uint32_t		byteorder;
>  	struct expr_ops		*ops;
>  	uint8_t			data[];
>  };
> @@ -15,5 +16,4 @@ struct nlmsghdr;
>  void nftnl_expr_build_payload(struct nlmsghdr *nlh, struct nftnl_expr *expr);
>  struct nftnl_expr *nftnl_expr_parse(struct nlattr *attr);
>
> -
>  #endif
> diff --git a/include/expr_ops.h b/include/expr_ops.h
> index 7a6aa23f9bd1d..161babdade596 100644
> --- a/include/expr_ops.h
> +++ b/include/expr_ops.h
> @@ -7,6 +7,7 @@
>  struct nlattr;
>  struct nlmsghdr;
>  struct nftnl_expr;
> +struct nftnl_print_ctx;
>
>  struct expr_ops {
>  	const char *name;
> @@ -18,6 +19,7 @@ struct expr_ops {
>  	const void *(*get)(const struct nftnl_expr *e, uint16_t type, uint32_t *data_len);
>  	int 	(*parse)(struct nftnl_expr *e, struct nlattr *attr);
>  	void	(*build)(struct nlmsghdr *nlh, const struct nftnl_expr *e);
> +	void	(*byteorder)(struct nftnl_byteorder_ctx *ctx, struct nftnl_expr *e);
>  	int	(*snprintf)(char *buf, size_t len, uint32_t flags, const struct nftnl_expr *e);
>  };
>
> diff --git a/src/expr.c b/src/expr.c
> index 277bbdeeb5d02..d958bab98e925 100644
> --- a/src/expr.c
> +++ b/src/expr.c
> @@ -301,3 +301,54 @@ 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);
>  }
> +
> +void nftnl_reg_byteorder_set(struct nftnl_byteorder_ctx *ctx, uint32_t reg,
> +			     enum nftnl_byteorder byteorder)
> +{
> +       if (reg > NFT_REG32_15)
> +	       return;
> +
> +       ctx->expr[reg].byteorder = byteorder;
> +}
> +
> +enum nftnl_byteorder nftnl_reg_byteorder_get(struct nftnl_byteorder_ctx *ctx,
> +					     uint32_t reg)
> +{
> +       if (reg > NFT_REG32_15)
> +	       return NFTNL_BYTEORDER_UNKNOWN;
> +
> +       return ctx->expr[reg].byteorder;
> +}
> +
> +void nftnl_reg_byteorder_unknown(struct nftnl_byteorder_ctx *ctx, uint32_t reg,
> +				 struct nftnl_expr *expr)
> +{
> +       int k;
> +
> +       if (reg > NFT_REG32_15)
> +	       return;
> +

This doesn't look right:

> +       k = ctx->num_pending++;
> +       if (k >= NFTNL_CTX_BYTEORDER_MAX_EXPRS)
> +	       return;

Shouldn't it be?

        if (ctx->num_pending >= NFTNL_CTX_BYTEORDER_MAX_EXPRS)
                return;
        k = ctx->num_pending++;

Otherwise ...

> +       ctx->pending[k].reg = reg;
> +       ctx->pending[k].expr = expr;
> +}
> +
> +void nftnl_reg_byteorder_resolve(struct nftnl_byteorder_ctx *ctx, uint32_t reg,
> +				 enum nftnl_byteorder byteorder)
> +{
> +       struct nftnl_expr *expr;
> +       int i;

... we can run off the end of `ctx->pending` here:

> +       for (i = 0; i < ctx->num_pending; i++) {
> +	       if (!ctx->pending[i].expr)
> +		       continue;
> +	       if (ctx->pending[i].reg == reg) {
> +		       expr = ctx->pending[i].expr;
> +		       expr->byteorder = byteorder;
> +		       ctx->pending[i].expr = NULL;
> +	       }
> +       }
> +}
> diff --git a/src/expr/bitwise.c b/src/expr/bitwise.c
> index d0c7827eacec9..a21dec01d286f 100644
> --- a/src/expr/bitwise.c
> +++ b/src/expr/bitwise.c
> @@ -209,9 +209,18 @@ nftnl_expr_bitwise_parse(struct nftnl_expr *e, struct nlattr *attr)
>  	return ret;
>  }
>
> +static void
> +nftnl_expr_bitwise_byteorder(struct nftnl_byteorder_ctx *ctx, struct nftnl_expr *e)
> +{
> +	struct nftnl_expr_bitwise *bitwise = nftnl_expr_data(e);
> +
> +	e->byteorder = nftnl_reg_byteorder_get(ctx, bitwise->sreg);
> +}
> +
>  static int
>  nftnl_expr_bitwise_snprintf_bool(char *buf, size_t remain,
> -				 const struct nftnl_expr_bitwise *bitwise)
> +				 const struct nftnl_expr_bitwise *bitwise,
> +				 enum nftnl_byteorder byteorder)
>  {
>  	int offset = 0, ret;
>
> @@ -220,14 +229,14 @@ nftnl_expr_bitwise_snprintf_bool(char *buf, size_t remain,
>  	SNPRINTF_BUFFER_SIZE(ret, remain, offset);
>
>  	ret = nftnl_data_reg_snprintf(buf + offset, remain, &bitwise->mask,
> -				      0, DATA_VALUE);
> +				      0, DATA_VALUE, byteorder);
>  	SNPRINTF_BUFFER_SIZE(ret, remain, offset);
>
>  	ret = snprintf(buf + offset, remain, ") ^ ");
>  	SNPRINTF_BUFFER_SIZE(ret, remain, offset);
>
>  	ret = nftnl_data_reg_snprintf(buf + offset, remain, &bitwise->xor,
> -				      0, DATA_VALUE);
> +				      0, DATA_VALUE, byteorder);
>  	SNPRINTF_BUFFER_SIZE(ret, remain, offset);
>
>  	return offset;
> @@ -235,7 +244,8 @@ nftnl_expr_bitwise_snprintf_bool(char *buf, size_t remain,
>
>  static int
>  nftnl_expr_bitwise_snprintf_shift(char *buf, size_t remain, const char *op,
> -				  const struct nftnl_expr_bitwise *bitwise)
> +				  const struct nftnl_expr_bitwise *bitwise,
> +				  enum nftnl_byteorder byteorder)
>  {	int offset = 0, ret;
>
>  	ret = snprintf(buf, remain, "reg %u = ( reg %u %s ",
> @@ -243,7 +253,7 @@ nftnl_expr_bitwise_snprintf_shift(char *buf, size_t remain, const char *op,
>  	SNPRINTF_BUFFER_SIZE(ret, remain, offset);
>
>  	ret = nftnl_data_reg_snprintf(buf + offset, remain, &bitwise->data,
> -				      0, DATA_VALUE);
> +				      0, DATA_VALUE, byteorder);
>  	SNPRINTF_BUFFER_SIZE(ret, remain, offset);
>
>  	ret = snprintf(buf + offset, remain, ") ");
> @@ -261,13 +271,16 @@ nftnl_expr_bitwise_snprintf(char *buf, size_t size,
>
>  	switch (bitwise->op) {
>  	case NFT_BITWISE_BOOL:
> -		err = nftnl_expr_bitwise_snprintf_bool(buf, size, bitwise);
> +		err = nftnl_expr_bitwise_snprintf_bool(buf, size, bitwise,
> +						       e->byteorder);
>  		break;
>  	case NFT_BITWISE_LSHIFT:
> -		err = nftnl_expr_bitwise_snprintf_shift(buf, size, "<<", bitwise);
> +		err = nftnl_expr_bitwise_snprintf_shift(buf, size, "<<",
> +							bitwise, e->byteorder);
>  		break;
>  	case NFT_BITWISE_RSHIFT:
> -		err = nftnl_expr_bitwise_snprintf_shift(buf, size, ">>", bitwise);
> +		err = nftnl_expr_bitwise_snprintf_shift(buf, size, ">>",
> +							bitwise, e->byteorder);
>  		break;
>  	}
>
> @@ -282,5 +295,6 @@ struct expr_ops expr_ops_bitwise = {
>  	.get		= nftnl_expr_bitwise_get,
>  	.parse		= nftnl_expr_bitwise_parse,
>  	.build		= nftnl_expr_bitwise_build,
> +	.byteorder	= nftnl_expr_bitwise_byteorder,
>  	.snprintf	= nftnl_expr_bitwise_snprintf,
>  };
> diff --git a/src/expr/byteorder.c b/src/expr/byteorder.c
> index d299745fc57b4..ae67ee6ef2b35 100644
> --- a/src/expr/byteorder.c
> +++ b/src/expr/byteorder.c
> @@ -197,6 +197,26 @@ static inline int nftnl_str2ntoh(const char *op)
>  	}
>  }
>
> +static void nftnl_expr_byteorder_byteorder(struct nftnl_byteorder_ctx *ctx,
> +					   struct nftnl_expr *e)
> +{
> +	struct nftnl_expr_byteorder *byteorder = nftnl_expr_data(e);
> +	enum nftnl_byteorder bo;
> +
> +	switch (byteorder->op) {
> +	case NFT_BYTEORDER_NTOH:
> +		bo = NFTNL_BYTEORDER_HOST;
> +		break;
> +	case NFT_BYTEORDER_HTON:
> +		bo = NFTNL_BYTEORDER_NETWORK;
> +		break;
> +	default:
> +		bo = NFTNL_BYTEORDER_UNKNOWN;
> +		break;
> +	}
> +	nftnl_reg_byteorder_set(ctx, byteorder->dreg, bo);
> +}
> +
>  static int
>  nftnl_expr_byteorder_snprintf(char *buf, size_t remain,
>  			      uint32_t flags, const struct nftnl_expr *e)
> @@ -220,5 +240,6 @@ struct expr_ops expr_ops_byteorder = {
>  	.get		= nftnl_expr_byteorder_get,
>  	.parse		= nftnl_expr_byteorder_parse,
>  	.build		= nftnl_expr_byteorder_build,
> +	.byteorder	= nftnl_expr_byteorder_byteorder,
>  	.snprintf	= nftnl_expr_byteorder_snprintf,
>  };
> diff --git a/src/expr/cmp.c b/src/expr/cmp.c
> index 6030693f15d86..846a112a03231 100644
> --- a/src/expr/cmp.c
> +++ b/src/expr/cmp.c
> @@ -176,6 +176,24 @@ static inline int nftnl_str2cmp(const char *op)
>  	}
>  }
>
> +static void
> +nftnl_expr_cmp_byteorder(struct nftnl_byteorder_ctx *ctx, struct nftnl_expr *e)
> +{
> +	struct nftnl_expr_cmp *cmp = nftnl_expr_data(e);
> +
> +	switch (cmp->op) {
> +	case NFT_CMP_LT:
> +	case NFT_CMP_LTE:
> +	case NFT_CMP_GT:
> +	case NFT_CMP_GTE:
> +		e->byteorder = NFTNL_BYTEORDER_NETWORK;
> +		break;
> +	default:
> +		e->byteorder = nftnl_reg_byteorder_get(ctx, cmp->sreg);
> +		break;
> +	}
> +}
> +
>  static int
>  nftnl_expr_cmp_snprintf(char *buf, size_t remain,
>  			uint32_t flags, const struct nftnl_expr *e)
> @@ -188,7 +206,7 @@ nftnl_expr_cmp_snprintf(char *buf, size_t remain,
>  	SNPRINTF_BUFFER_SIZE(ret, remain, offset);
>
>  	ret = nftnl_data_reg_snprintf(buf + offset, remain, &cmp->data,
> -				      0, DATA_VALUE);
> +				      0, DATA_VALUE, e->byteorder);
>  	SNPRINTF_BUFFER_SIZE(ret, remain, offset);
>
>  	return offset;
> @@ -202,5 +220,6 @@ struct expr_ops expr_ops_cmp = {
>  	.get		= nftnl_expr_cmp_get,
>  	.parse		= nftnl_expr_cmp_parse,
>  	.build		= nftnl_expr_cmp_build,
> +	.byteorder	= nftnl_expr_cmp_byteorder,
>  	.snprintf	= nftnl_expr_cmp_snprintf,
>  };
> diff --git a/src/expr/ct.c b/src/expr/ct.c
> index d5dfc81cfe0d1..fe4fc43688eb5 100644
> --- a/src/expr/ct.c
> +++ b/src/expr/ct.c
> @@ -222,6 +222,35 @@ static inline int str2ctdir(const char *str, uint8_t *ctdir)
>  	return -1;
>  }
>
> +static void
> +nftnl_expr_ct_byteorder(struct nftnl_byteorder_ctx *ctx, struct nftnl_expr *e)
> +{
> +	struct nftnl_expr_ct *ct = nftnl_expr_data(e);
> +
> +	switch (ct->key) {
> +	case NFT_CT_HELPER:
> +	case NFT_CT_SRC:
> +	case NFT_CT_DST:
> +	case NFT_CT_PROTOCOL:
> +	case NFT_CT_PROTO_SRC:
> +	case NFT_CT_PROTO_DST:
> +	case NFT_CT_SRC_IP:
> +	case NFT_CT_DST_IP:
> +	case NFT_CT_SRC_IP6:
> +	case NFT_CT_DST_IP6:
> +	case NFT_CT_ID:
> +		e->byteorder = NFTNL_BYTEORDER_NETWORK;
> +		break;
> +	default:
> +		e->byteorder = NFTNL_BYTEORDER_HOST;
> +	}
> +
> +	if (e->flags & (1 << NFTNL_EXPR_CT_SREG))
> +		nftnl_reg_byteorder_resolve(ctx, ct->sreg, e->byteorder);
> +	if (e->flags & (1 << NFTNL_EXPR_CT_DREG))
> +		nftnl_reg_byteorder_set(ctx, ct->dreg, e->byteorder);
> +}
> +
>  static int
>  nftnl_expr_ct_snprintf(char *buf, size_t remain,
>  		       uint32_t flags, const struct nftnl_expr *e)
> @@ -258,5 +287,6 @@ struct expr_ops expr_ops_ct = {
>  	.get		= nftnl_expr_ct_get,
>  	.parse		= nftnl_expr_ct_parse,
>  	.build		= nftnl_expr_ct_build,
> +	.byteorder	= nftnl_expr_ct_byteorder,
>  	.snprintf	= nftnl_expr_ct_snprintf,
>  };
> diff --git a/src/expr/data_reg.c b/src/expr/data_reg.c
> index 2633a775c90cc..789958ea66c0d 100644
> --- a/src/expr/data_reg.c
> +++ b/src/expr/data_reg.c
> @@ -27,16 +27,22 @@
>  static int
>  nftnl_data_reg_value_snprintf_default(char *buf, size_t remain,
>  				      const union nftnl_data_reg *reg,
> -				      uint32_t flags)
> +				      uint32_t flags,
> +				      enum nftnl_byteorder byteorder)
>  {
>  	const char *pfx = flags & DATA_F_NOPFX ? "" : "0x";
>  	int offset = 0, ret, i;
> +	uint32_t value;
>
>
>
>  	for (i = 0; i < div_round_up(reg->len, sizeof(uint32_t)); i++) {
> -		ret = snprintf(buf + offset, remain,
> -			       "%s%.8x ", pfx, reg->val[i]);
> +		if (byteorder == NFTNL_BYTEORDER_NETWORK)
> +			value = ntohl(reg->val[i]);
> +		else
> +			value = reg->val[i];
> +
> +		ret = snprintf(buf + offset, remain, "%s%.8x ", pfx, value);
>  		SNPRINTF_BUFFER_SIZE(ret, remain, offset);
>  	}
>
> @@ -63,12 +69,14 @@ nftnl_data_reg_verdict_snprintf_def(char *buf, size_t size,
>
>  int nftnl_data_reg_snprintf(char *buf, size_t size,
>  			    const union nftnl_data_reg *reg,
> -			    uint32_t flags, int reg_type)
> +			    uint32_t flags, int reg_type,
> +			    enum nftnl_byteorder byteorder)
>  {
>  	switch(reg_type) {
>  	case DATA_VALUE:
>  		return nftnl_data_reg_value_snprintf_default(buf, size,
> -							     reg, flags);
> +							     reg, flags,
> +							     byteorder);
>  	case DATA_VERDICT:
>  	case DATA_CHAIN:
>  		return nftnl_data_reg_verdict_snprintf_def(buf, size,
> diff --git a/src/expr/dup.c b/src/expr/dup.c
> index f041b551a7e78..58c3fe6989e91 100644
> --- a/src/expr/dup.c
> +++ b/src/expr/dup.c
> @@ -111,6 +111,19 @@ static int nftnl_expr_dup_parse(struct nftnl_expr *e, struct nlattr *attr)
>  	return ret;
>  }
>
> +static void
> +nftnl_expr_dup_byteorder(struct nftnl_byteorder_ctx *ctx, struct nftnl_expr *e)
> +{
> +	struct nftnl_expr_dup *dup = nftnl_expr_data(e);
> +
> +	if (e->flags & (1 << NFTNL_EXPR_DUP_SREG_ADDR))
> +		nftnl_reg_byteorder_resolve(ctx, dup->sreg_addr,
> +					    NFTNL_BYTEORDER_NETWORK);
> +	if (e->flags & (1 << NFTNL_EXPR_DUP_SREG_DEV))
> +		nftnl_reg_byteorder_resolve(ctx, dup->sreg_dev,
> +					    NFTNL_BYTEORDER_HOST);
> +}
> +
>  static int nftnl_expr_dup_snprintf(char *buf, size_t remain,
>  				   uint32_t flags, const struct nftnl_expr *e)
>  {
> @@ -138,5 +151,6 @@ struct expr_ops expr_ops_dup = {
>  	.get		= nftnl_expr_dup_get,
>  	.parse		= nftnl_expr_dup_parse,
>  	.build		= nftnl_expr_dup_build,
> +	.byteorder	= nftnl_expr_dup_byteorder,
>  	.snprintf	= nftnl_expr_dup_snprintf,
>  };
> diff --git a/src/expr/exthdr.c b/src/expr/exthdr.c
> index 1b813b1e47c4d..280fe73f48041 100644
> --- a/src/expr/exthdr.c
> +++ b/src/expr/exthdr.c
> @@ -235,6 +235,19 @@ static inline int str2exthdr_type(const char *str)
>  	return -1;
>  }
>
> +static void
> +nftnl_expr_exthdr_byteorder(struct nftnl_byteorder_ctx *ctx, struct nftnl_expr *e)
> +{
> +	struct nftnl_expr_exthdr *exthdr = nftnl_expr_data(e);
> +
> +	e->byteorder = NFTNL_BYTEORDER_NETWORK;
> +
> +	if (e->flags & (1 << NFTNL_EXPR_EXTHDR_DREG))

Spaces instead of tabs here:

> +                nftnl_reg_byteorder_set(ctx, exthdr->dreg, e->byteorder);
> +	if (e->flags & (1 << NFTNL_EXPR_EXTHDR_SREG))
> +		nftnl_reg_byteorder_resolve(ctx, exthdr->sreg, e->byteorder);
> +}
> +
>  static int
>  nftnl_expr_exthdr_snprintf(char *buf, size_t len,
>  			   uint32_t flags, const struct nftnl_expr *e)
> @@ -262,5 +275,6 @@ struct expr_ops expr_ops_exthdr = {
>  	.get		= nftnl_expr_exthdr_get,
>  	.parse		= nftnl_expr_exthdr_parse,
>  	.build		= nftnl_expr_exthdr_build,
> +	.byteorder	= nftnl_expr_exthdr_byteorder,
>  	.snprintf	= nftnl_expr_exthdr_snprintf,
>  };
> diff --git a/src/expr/fib.c b/src/expr/fib.c
> index aaff52acabdbd..75194bff95f41 100644
> --- a/src/expr/fib.c
> +++ b/src/expr/fib.c
> @@ -128,6 +128,23 @@ nftnl_expr_fib_parse(struct nftnl_expr *e, struct nlattr *attr)
>  	return ret;
>  }
>
> +static void
> +nftnl_expr_fib_byteorder(struct nftnl_byteorder_ctx *ctx, struct nftnl_expr *e)
> +{
> +	struct nftnl_expr_fib *fib = nftnl_expr_data(e);
> +
> +	switch (fib->result) {
> +	case NFT_FIB_RESULT_OIFNAME:
> +		e->byteorder = NFTNL_BYTEORDER_NETWORK;
> +		break;
> +	default:
> +		e->byteorder = NFTNL_BYTEORDER_HOST;
> +	}
> +
> +	if (e->flags & (1 << NFTNL_EXPR_FIB_DREG))
> +                nftnl_reg_byteorder_set(ctx, fib->dreg, e->byteorder);
> +}
> +
>  static const char *fib_type[NFT_FIB_RESULT_MAX + 1] = {
>  	[NFT_FIB_RESULT_OIF] = "oif",
>  	[NFT_FIB_RESULT_OIFNAME] = "oifname",
> @@ -198,5 +215,6 @@ struct expr_ops expr_ops_fib = {
>  	.get		= nftnl_expr_fib_get,
>  	.parse		= nftnl_expr_fib_parse,
>  	.build		= nftnl_expr_fib_build,
> +	.byteorder	= nftnl_expr_fib_byteorder,
>  	.snprintf	= nftnl_expr_fib_snprintf,
>  };
> diff --git a/src/expr/fwd.c b/src/expr/fwd.c
> index 82e5a418bfae5..6875abfa80d19 100644
> --- a/src/expr/fwd.c
> +++ b/src/expr/fwd.c
> @@ -125,6 +125,19 @@ static int nftnl_expr_fwd_parse(struct nftnl_expr *e, struct nlattr *attr)
>  	return ret;
>  }
>
> +static void
> +nftnl_expr_fwd_byteorder(struct nftnl_byteorder_ctx *ctx, struct nftnl_expr *e)
> +{
> +	struct nftnl_expr_fwd *fwd = nftnl_expr_data(e);
> +
> +	if (e->flags & (1 << NFTNL_EXPR_FWD_SREG_DEV))
> +		nftnl_reg_byteorder_resolve(ctx, fwd->sreg_dev,
> +					    NFTNL_BYTEORDER_HOST);
> +	if (e->flags & (1 << NFTNL_EXPR_FWD_SREG_ADDR))
> +		nftnl_reg_byteorder_resolve(ctx, fwd->sreg_addr,
> +					    NFTNL_BYTEORDER_NETWORK);
> +}
> +
>  static int nftnl_expr_fwd_snprintf(char *buf, size_t remain,
>  				   uint32_t flags, const struct nftnl_expr *e)
>  {
> @@ -158,5 +171,6 @@ struct expr_ops expr_ops_fwd = {
>  	.get		= nftnl_expr_fwd_get,
>  	.parse		= nftnl_expr_fwd_parse,
>  	.build		= nftnl_expr_fwd_build,
> +	.byteorder	= nftnl_expr_fwd_byteorder,
>  	.snprintf	= nftnl_expr_fwd_snprintf,
>  };
> diff --git a/src/expr/immediate.c b/src/expr/immediate.c
> index 94b043c0fc8ab..fb291c7606bab 100644
> --- a/src/expr/immediate.c
> +++ b/src/expr/immediate.c
> @@ -183,6 +183,14 @@ nftnl_expr_immediate_parse(struct nftnl_expr *e, struct nlattr *attr)
>  	return ret;
>  }
>
> +static void
> +nftnl_expr_immediate_byteorder(struct nftnl_byteorder_ctx *ctx, struct nftnl_expr *e)
> +{
> +	struct nftnl_expr_immediate *imm = nftnl_expr_data(e);
> +
> +	nftnl_reg_byteorder_unknown(ctx, imm->dreg, e);
> +}
> +
>  static int
>  nftnl_expr_immediate_snprintf(char *buf, size_t remain,
>  			      uint32_t flags, const struct nftnl_expr *e)
> @@ -195,17 +203,19 @@ nftnl_expr_immediate_snprintf(char *buf, size_t remain,
>
>  	if (e->flags & (1 << NFTNL_EXPR_IMM_DATA)) {
>  		ret = nftnl_data_reg_snprintf(buf + offset, remain, &imm->data,
> -					      flags, DATA_VALUE);
> +					      flags, DATA_VALUE, e->byteorder);
>  		SNPRINTF_BUFFER_SIZE(ret, remain, offset);
>
>  	} else if (e->flags & (1 << NFTNL_EXPR_IMM_VERDICT)) {
>  		ret = nftnl_data_reg_snprintf(buf + offset, remain, &imm->data,
> -					      flags, DATA_VERDICT);
> +					      flags, DATA_VERDICT,
> +					      NFTNL_BYTEORDER_UNKNOWN);
>  		SNPRINTF_BUFFER_SIZE(ret, remain, offset);
>
>  	} else if (e->flags & (1 << NFTNL_EXPR_IMM_CHAIN)) {
>  		ret = nftnl_data_reg_snprintf(buf + offset, remain, &imm->data,
> -					      flags, DATA_CHAIN);
> +					      flags, DATA_CHAIN,
> +					      NFTNL_BYTEORDER_UNKNOWN);
>  		SNPRINTF_BUFFER_SIZE(ret, remain, offset);
>  	}
>
> @@ -229,5 +239,6 @@ struct expr_ops expr_ops_immediate = {
>  	.get		= nftnl_expr_immediate_get,
>  	.parse		= nftnl_expr_immediate_parse,
>  	.build		= nftnl_expr_immediate_build,
> +	.byteorder	= nftnl_expr_immediate_byteorder,
>  	.snprintf	= nftnl_expr_immediate_snprintf,
>  };
> diff --git a/src/expr/masq.c b/src/expr/masq.c
> index 684708c758390..a964e3ae1a938 100644
> --- a/src/expr/masq.c
> +++ b/src/expr/masq.c
> @@ -131,6 +131,21 @@ nftnl_expr_masq_parse(struct nftnl_expr *e, struct nlattr *attr)
>  	return 0;
>  }
>
> +static void
> +nftnl_expr_masq_byteorder(struct nftnl_byteorder_ctx *ctx, struct nftnl_expr *e)
> +{
> +	struct nftnl_expr_masq *masq = nftnl_expr_data(e);
> +
> +	if (e->flags & (1 << NFTNL_EXPR_MASQ_REG_PROTO_MIN))
> +		nftnl_reg_byteorder_resolve(ctx, masq->sreg_proto_min,
> +					    NFTNL_BYTEORDER_NETWORK);
> +	if (e->flags & (1 << NFTNL_EXPR_MASQ_REG_PROTO_MAX))
> +		nftnl_reg_byteorder_resolve(ctx, masq->sreg_proto_max,
> +					    NFTNL_BYTEORDER_NETWORK);
> +
> +	e->byteorder = NFTNL_BYTEORDER_NETWORK;
> +}
> +
>  static int nftnl_expr_masq_snprintf(char *buf, size_t remain,
>  				    uint32_t flags, const struct nftnl_expr *e)
>  {
> @@ -163,5 +178,6 @@ struct expr_ops expr_ops_masq = {
>  	.get		= nftnl_expr_masq_get,
>  	.parse		= nftnl_expr_masq_parse,
>  	.build		= nftnl_expr_masq_build,
> +	.byteorder	= nftnl_expr_masq_byteorder,
>  	.snprintf	= nftnl_expr_masq_snprintf,
>  };
> diff --git a/src/expr/meta.c b/src/expr/meta.c
> index 34fbb9bb63c03..deb14e5cb054d 100644
> --- a/src/expr/meta.c
> +++ b/src/expr/meta.c
> @@ -191,6 +191,33 @@ static inline int str2meta_key(const char *str)
>  	return -1;
>  }
>
> +static void nftnl_expr_meta_byteorder(struct nftnl_byteorder_ctx *ctx,
> +				      struct nftnl_expr *e)
> +{
> +	struct nftnl_expr_meta *meta = nftnl_expr_data(e);
> +
> +
> +	switch (meta->key) {
> +	case NFT_META_PROTOCOL:
> +	case NFT_META_IIFNAME:
> +	case NFT_META_OIFNAME:
> +	case NFT_META_BRI_IIFNAME:
> +	case NFT_META_BRI_OIFNAME:
> +	case NFT_META_PRANDOM:
> +	case NFT_META_BRI_IIFVPROTO:
> +	case NFT_META_SDIFNAME:
> +		e->byteorder = NFTNL_BYTEORDER_NETWORK;
> +		break;
> +	default:
> +		e->byteorder = NFTNL_BYTEORDER_HOST;
> +	}
> +
> +	if (e->flags & (1 << NFTNL_EXPR_META_SREG))
> +		nftnl_reg_byteorder_resolve(ctx, meta->sreg, e->byteorder);
> +	if (e->flags & (1 << NFTNL_EXPR_META_DREG))
> +		nftnl_reg_byteorder_set(ctx, meta->dreg, e->byteorder);
> +}
> +
>  static int
>  nftnl_expr_meta_snprintf(char *buf, size_t len,
>  			 uint32_t flags, const struct nftnl_expr *e)
> @@ -216,5 +243,6 @@ struct expr_ops expr_ops_meta = {
>  	.get		= nftnl_expr_meta_get,
>  	.parse		= nftnl_expr_meta_parse,
>  	.build		= nftnl_expr_meta_build,
> +	.byteorder	= nftnl_expr_meta_byteorder,
>  	.snprintf	= nftnl_expr_meta_snprintf,
>  };
> diff --git a/src/expr/nat.c b/src/expr/nat.c
> index 0a9cdd7f65f8f..785ceb20a92bf 100644
> --- a/src/expr/nat.c
> +++ b/src/expr/nat.c
> @@ -220,6 +220,27 @@ static inline int nftnl_str2nat(const char *nat)
>  	}
>  }
>
> +static void
> +nftnl_expr_nat_byteorder(struct nftnl_byteorder_ctx *ctx, struct nftnl_expr *e)
> +{
> +	struct nftnl_expr_nat *nat = nftnl_expr_data(e);
> +
> +	if (e->flags & (1 << NFTNL_EXPR_NAT_REG_ADDR_MIN))
> +		nftnl_reg_byteorder_resolve(ctx, nat->sreg_addr_min,
> +					    NFTNL_BYTEORDER_NETWORK);
> +	if (e->flags & (1 << NFTNL_EXPR_NAT_REG_ADDR_MAX))
> +		nftnl_reg_byteorder_resolve(ctx, nat->sreg_addr_max,
> +					    NFTNL_BYTEORDER_NETWORK);
> +	if (e->flags & (1 << NFTNL_EXPR_NAT_REG_PROTO_MIN))
> +		nftnl_reg_byteorder_resolve(ctx, nat->sreg_proto_min,
> +					    NFTNL_BYTEORDER_NETWORK);
> +	if (e->flags & (1 << NFTNL_EXPR_NAT_REG_PROTO_MAX))
> +		nftnl_reg_byteorder_resolve(ctx, nat->sreg_proto_max,
> +					    NFTNL_BYTEORDER_NETWORK);
> +
> +	e->byteorder = NFTNL_BYTEORDER_NETWORK;
> +}
> +
>  static int
>  nftnl_expr_nat_snprintf(char *buf, size_t remain,
>  			uint32_t flags, const struct nftnl_expr *e)
> @@ -274,5 +295,6 @@ struct expr_ops expr_ops_nat = {
>  	.get		= nftnl_expr_nat_get,
>  	.parse		= nftnl_expr_nat_parse,
>  	.build		= nftnl_expr_nat_build,
> +	.byteorder	= nftnl_expr_nat_byteorder,
>  	.snprintf	= nftnl_expr_nat_snprintf,
>  };
> diff --git a/src/expr/numgen.c b/src/expr/numgen.c
> index 159dfeca3618b..dfbeeaf1b172a 100644
> --- a/src/expr/numgen.c
> +++ b/src/expr/numgen.c
> @@ -142,6 +142,17 @@ nftnl_expr_ng_parse(struct nftnl_expr *e, struct nlattr *attr)
>  	return ret;
>  }
>
> +static void
> +nftnl_expr_ng_byteorder(struct nftnl_byteorder_ctx *ctx, struct nftnl_expr *e)
> +{
> +	struct nftnl_expr_ng *ng = nftnl_expr_data(e);
> +
> +	e->byteorder = NFTNL_BYTEORDER_HOST;
> +
> +	if (e->flags & (1 << NFTNL_EXPR_NG_DREG))
> +		nftnl_reg_byteorder_set(ctx, ng->dreg, e->byteorder);
> +}
> +
>  static int
>  nftnl_expr_ng_snprintf(char *buf, size_t remain,
>  		       uint32_t flags, const struct nftnl_expr *e)
> @@ -180,5 +191,6 @@ struct expr_ops expr_ops_ng = {
>  	.get		= nftnl_expr_ng_get,
>  	.parse		= nftnl_expr_ng_parse,
>  	.build		= nftnl_expr_ng_build,
> +	.byteorder	= nftnl_expr_ng_byteorder,
>  	.snprintf	= nftnl_expr_ng_snprintf,
>  };
> diff --git a/src/expr/osf.c b/src/expr/osf.c
> index 215a681a97aae..b2e4294877c05 100644
> --- a/src/expr/osf.c
> +++ b/src/expr/osf.c
> @@ -124,6 +124,17 @@ nftnl_expr_osf_parse(struct nftnl_expr *e, struct nlattr *attr)
>  	return 0;
>  }
>
> +static void
> +nftnl_expr_osf_byteorder(struct nftnl_byteorder_ctx *ctx, struct nftnl_expr *e)
> +{
> +	struct nftnl_expr_osf *osf = nftnl_expr_data(e);
> +
> +	e->byteorder = NFTNL_BYTEORDER_NETWORK;
> +
> +	if (e->flags & (1 << NFTNL_EXPR_OSF_DREG))
> +                nftnl_reg_byteorder_set(ctx, osf->dreg, e->byteorder);
> +}
> +
>  static int
>  nftnl_expr_osf_snprintf(char *buf, size_t len,
>  			uint32_t flags, const struct nftnl_expr *e)
> @@ -147,5 +158,6 @@ struct expr_ops expr_ops_osf = {
>  	.get		= nftnl_expr_osf_get,
>  	.parse		= nftnl_expr_osf_parse,
>  	.build		= nftnl_expr_osf_build,
> +	.byteorder	= nftnl_expr_osf_byteorder,
>  	.snprintf	= nftnl_expr_osf_snprintf,
>  };
> diff --git a/src/expr/payload.c b/src/expr/payload.c
> index 82747ec8994f7..84764e837a965 100644
> --- a/src/expr/payload.c
> +++ b/src/expr/payload.c
> @@ -232,6 +232,19 @@ static inline int nftnl_str2base(const char *base)
>  	}
>  }
>
> +static void
> +nftnl_expr_payload_byteorder(struct nftnl_byteorder_ctx *ctx, struct nftnl_expr *e)
> +{
> +	struct nftnl_expr_payload *payload = nftnl_expr_data(e);
> +
> +	e->byteorder = NFTNL_BYTEORDER_NETWORK;
> +
> +	if (payload->sreg)
> +		nftnl_reg_byteorder_resolve(ctx, payload->sreg, e->byteorder);
> +	else

Spaces instead of tabs here:

> +                nftnl_reg_byteorder_set(ctx, payload->dreg, e->byteorder);
> +}
> +
>  static int
>  nftnl_expr_payload_snprintf(char *buf, size_t len,
>  			    uint32_t flags, const struct nftnl_expr *e)
> @@ -259,5 +272,6 @@ struct expr_ops expr_ops_payload = {
>  	.get		= nftnl_expr_payload_get,
>  	.parse		= nftnl_expr_payload_parse,
>  	.build		= nftnl_expr_payload_build,
> +	.byteorder	= nftnl_expr_payload_byteorder,
>  	.snprintf	= nftnl_expr_payload_snprintf,
>  };
> diff --git a/src/expr/queue.c b/src/expr/queue.c
> index 8f70977f7de85..1a65c8ad6484a 100644
> --- a/src/expr/queue.c
> +++ b/src/expr/queue.c
> @@ -143,6 +143,17 @@ nftnl_expr_queue_parse(struct nftnl_expr *e, struct nlattr *attr)
>  	return 0;
>  }
>
> +static void
> +nftnl_expr_queue_byteorder(struct nftnl_byteorder_ctx *ctx, struct nftnl_expr *e)
> +{
> +	struct nftnl_expr_queue *queue = nftnl_expr_data(e);
> +
> +	e->byteorder = NFTNL_BYTEORDER_HOST;
> +
> +	if (e->flags & (1 << NFTNL_EXPR_QUEUE_SREG_QNUM))
> +                nftnl_reg_byteorder_resolve(ctx, queue->sreg_qnum, e->byteorder);
> +}
> +
>  static int
>  nftnl_expr_queue_snprintf(char *buf, size_t remain,
>  			  uint32_t flags, const struct nftnl_expr *e)
> @@ -193,5 +204,6 @@ struct expr_ops expr_ops_queue = {
>  	.get		= nftnl_expr_queue_get,
>  	.parse		= nftnl_expr_queue_parse,
>  	.build		= nftnl_expr_queue_build,
> +	.byteorder	= nftnl_expr_queue_byteorder,
>  	.snprintf	= nftnl_expr_queue_snprintf,
>  };
> diff --git a/src/expr/range.c b/src/expr/range.c
> index f76843a8afd0c..ab4e2e70d8d01 100644
> --- a/src/expr/range.c
> +++ b/src/expr/range.c
> @@ -184,6 +184,12 @@ static inline int nftnl_str2range(const char *op)
>  	}
>  }
>
> +static void nftnl_expr_range_byteorder(struct nftnl_byteorder_ctx *ctx,
> +				       struct nftnl_expr *e)
> +{
> +	e->byteorder = NFTNL_BYTEORDER_NETWORK;
> +}
> +
>  static int nftnl_expr_range_snprintf(char *buf, size_t remain,
>  				     uint32_t flags, const struct nftnl_expr *e)
>  {
> @@ -195,11 +201,11 @@ static int nftnl_expr_range_snprintf(char *buf, size_t remain,
>  	SNPRINTF_BUFFER_SIZE(ret, remain, offset);
>
>  	ret = nftnl_data_reg_snprintf(buf + offset, remain, &range->data_from,
> -				      0, DATA_VALUE);
> +				      0, DATA_VALUE, e->byteorder);
>  	SNPRINTF_BUFFER_SIZE(ret, remain, offset);
>
>  	ret = nftnl_data_reg_snprintf(buf + offset, remain, &range->data_to,
> -				      0, DATA_VALUE);
> +				      0, DATA_VALUE, e->byteorder);
>  	SNPRINTF_BUFFER_SIZE(ret, remain, offset);
>
>  	return offset;
> @@ -213,5 +219,6 @@ struct expr_ops expr_ops_range = {
>  	.get		= nftnl_expr_range_get,
>  	.parse		= nftnl_expr_range_parse,
>  	.build		= nftnl_expr_range_build,
> +	.byteorder	= nftnl_expr_range_byteorder,
>  	.snprintf	= nftnl_expr_range_snprintf,
>  };
> diff --git a/src/expr/redir.c b/src/expr/redir.c
> index 4f56cb4302b30..3c1ebc13909e5 100644
> --- a/src/expr/redir.c
> +++ b/src/expr/redir.c
> @@ -131,6 +131,21 @@ nftnl_expr_redir_parse(struct nftnl_expr *e, struct nlattr *attr)
>  	return 0;
>  }
>
> +static void
> +nftnl_expr_redir_byteorder(struct nftnl_byteorder_ctx *ctx, struct nftnl_expr *e)
> +{
> +	struct nftnl_expr_redir *redir = nftnl_expr_data(e);
> +
> +	if (e->flags & (1 << NFTNL_EXPR_REDIR_REG_PROTO_MIN))
> +		nftnl_reg_byteorder_resolve(ctx, redir->sreg_proto_min,
> +					    NFTNL_BYTEORDER_NETWORK);
> +	if (e->flags & (1 << NFTNL_EXPR_REDIR_REG_PROTO_MAX))
> +		nftnl_reg_byteorder_resolve(ctx, redir->sreg_proto_max,
> +					    NFTNL_BYTEORDER_NETWORK);
> +
> +	e->byteorder = NFTNL_BYTEORDER_NETWORK;
> +}
> +
>  static int
>  nftnl_expr_redir_snprintf(char *buf, size_t remain,
>  			  uint32_t flags, const struct nftnl_expr *e)
> @@ -167,5 +182,6 @@ struct expr_ops expr_ops_redir = {
>  	.get		= nftnl_expr_redir_get,
>  	.parse		= nftnl_expr_redir_parse,
>  	.build		= nftnl_expr_redir_build,
> +	.byteorder	= nftnl_expr_redir_byteorder,
>  	.snprintf	= nftnl_expr_redir_snprintf,
>  };
> diff --git a/src/expr/rt.c b/src/expr/rt.c
> index 1ad9b2ad4043f..bd8b2a91948ee 100644
> --- a/src/expr/rt.c
> +++ b/src/expr/rt.c
> @@ -112,6 +112,24 @@ nftnl_expr_rt_parse(struct nftnl_expr *e, struct nlattr *attr)
>  	return 0;
>  }
>
> +static void nftnl_expr_rt_byteorder(struct nftnl_byteorder_ctx *ctx,
> +				    struct nftnl_expr *e)
> +{
> +	struct nftnl_expr_rt *rt = nftnl_expr_data(e);
> +
> +	switch (rt->key) {
> +	case NFT_RT_NEXTHOP4:
> +	case NFT_RT_NEXTHOP6:
> +		e->byteorder = NFTNL_BYTEORDER_NETWORK;
> +		break;
> +	default:
> +		e->byteorder = NFTNL_BYTEORDER_HOST;
> +	}
> +
> +	if (e->flags & (1 << NFTNL_EXPR_RT_DREG))
> +		nftnl_reg_byteorder_set(ctx, rt->dreg, e->byteorder);
> +}
> +
>  static const char *rt_key2str_array[NFT_RT_MAX + 1] = {
>  	[NFT_RT_CLASSID]	= "classid",
>  	[NFT_RT_NEXTHOP4]	= "nexthop4",
> @@ -162,5 +180,6 @@ struct expr_ops expr_ops_rt = {
>  	.get		= nftnl_expr_rt_get,
>  	.parse		= nftnl_expr_rt_parse,
>  	.build		= nftnl_expr_rt_build,
> +	.byteorder	= nftnl_expr_rt_byteorder,
>  	.snprintf	= nftnl_expr_rt_snprintf,
>  };
> diff --git a/src/expr/socket.c b/src/expr/socket.c
> index 02d86f8ac57c0..dae9ea22bd24f 100644
> --- a/src/expr/socket.c
> +++ b/src/expr/socket.c
> @@ -126,6 +126,17 @@ nftnl_expr_socket_parse(struct nftnl_expr *e, struct nlattr *attr)
>  	return 0;
>  }
>
> +static void
> +nftnl_expr_socket_byteorder(struct nftnl_byteorder_ctx *ctx, struct nftnl_expr *e)
> +{
> +	struct nftnl_expr_socket *socket = nftnl_expr_data(e);
> +
> +	e->byteorder = NFTNL_BYTEORDER_HOST;
> +
> +	if (e->flags & (1 << NFTNL_EXPR_SOCKET_DREG))
> +                nftnl_reg_byteorder_set(ctx, socket->dreg, e->byteorder);
> +}
> +
>  static const char *socket_key2str_array[NFT_SOCKET_MAX + 1] = {
>  	[NFT_SOCKET_TRANSPARENT] = "transparent",
>  	[NFT_SOCKET_MARK] = "mark",
> @@ -165,5 +176,6 @@ struct expr_ops expr_ops_socket = {
>  	.get		= nftnl_expr_socket_get,
>  	.parse		= nftnl_expr_socket_parse,
>  	.build		= nftnl_expr_socket_build,
> +	.byteorder	= nftnl_expr_socket_byteorder,
>  	.snprintf	= nftnl_expr_socket_snprintf,
>  };
> diff --git a/src/expr/tproxy.c b/src/expr/tproxy.c
> index d3ee8f89b6db3..ef3e20569c1cf 100644
> --- a/src/expr/tproxy.c
> +++ b/src/expr/tproxy.c
> @@ -134,6 +134,19 @@ nftnl_expr_tproxy_build(struct nlmsghdr *nlh, const struct nftnl_expr *e)
>  				 htonl(tproxy->sreg_port));
>  }
>
> +static void nftnl_expr_tproxy_byteorder(struct nftnl_byteorder_ctx *ctx,
> +				        struct nftnl_expr *e)
> +{
> +	struct nftnl_expr_tproxy *t = nftnl_expr_data(e);
> +
> +	e->byteorder = NFTNL_BYTEORDER_NETWORK;
> +
> +	if (e->flags & (1 << NFTNL_EXPR_TPROXY_REG_ADDR))
> +		nftnl_reg_byteorder_resolve(ctx, t->sreg_addr, e->byteorder);
> +	if (e->flags & (1 << NFTNL_EXPR_TPROXY_REG_PORT))
> +		nftnl_reg_byteorder_resolve(ctx, t->sreg_port, e->byteorder);
> +}
> +
>  static int
>  nftnl_expr_tproxy_snprintf(char *buf, size_t remain,
>  			uint32_t flags, const struct nftnl_expr *e)
> @@ -170,5 +183,6 @@ struct expr_ops expr_ops_tproxy = {
>  	.get		= nftnl_expr_tproxy_get,
>  	.parse		= nftnl_expr_tproxy_parse,
>  	.build		= nftnl_expr_tproxy_build,
> +	.byteorder	= nftnl_expr_tproxy_byteorder,
>  	.snprintf	= nftnl_expr_tproxy_snprintf,
>  };
> diff --git a/src/expr/tunnel.c b/src/expr/tunnel.c
> index 1460fd26b0fbc..94a6c9bb4add2 100644
> --- a/src/expr/tunnel.c
> +++ b/src/expr/tunnel.c
> @@ -111,6 +111,17 @@ nftnl_expr_tunnel_parse(struct nftnl_expr *e, struct nlattr *attr)
>  	return 0;
>  }
>
> +static void
> +nftnl_expr_tunnel_byteorder(struct nftnl_byteorder_ctx *ctx, struct nftnl_expr *e)
> +{
> +	struct nftnl_expr_tunnel *tunnel = nftnl_expr_data(e);
> +
> +	e->byteorder = NFTNL_BYTEORDER_HOST;
> +
> +	if (e->flags & (1 << NFTNL_EXPR_TUNNEL_DREG))
> +                nftnl_reg_byteorder_set(ctx, tunnel->dreg, e->byteorder);
> +}
> +
>  static const char *tunnel_key2str_array[NFT_TUNNEL_MAX + 1] = {
>  	[NFT_TUNNEL_PATH]	= "path",
>  	[NFT_TUNNEL_ID]		= "id",
> @@ -145,5 +156,6 @@ struct expr_ops expr_ops_tunnel = {
>  	.get		= nftnl_expr_tunnel_get,
>  	.parse		= nftnl_expr_tunnel_parse,
>  	.build		= nftnl_expr_tunnel_build,
> +	.byteorder	= nftnl_expr_tunnel_byteorder,
>  	.snprintf	= nftnl_expr_tunnel_snprintf,
>  };
> diff --git a/src/expr/xfrm.c b/src/expr/xfrm.c
> index c81d14d638dcd..3134b076f042e 100644
> --- a/src/expr/xfrm.c
> +++ b/src/expr/xfrm.c
> @@ -171,6 +171,23 @@ static const char *xfrmdir2str(uint8_t dir)
>  	return xfrmdir2str_array[dir];
>  }
>
> +static void
> +nftnl_expr_xfrm_byteorder(struct nftnl_byteorder_ctx *ctx, struct nftnl_expr *e)
> +{
> +	struct nftnl_expr_xfrm *x = nftnl_expr_data(e);
> +
> +	switch (x->key) {
> +	case NFT_XFRM_KEY_REQID:
> +		e->byteorder = NFTNL_BYTEORDER_HOST;
> +		break;
> +	default:
> +		e->byteorder = NFTNL_BYTEORDER_NETWORK;
> +	}
> +
> +	if (e->flags & (1 << NFTNL_EXPR_XFRM_DREG))
> +                nftnl_reg_byteorder_set(ctx, x->dreg, e->byteorder);
> +}
> +
>  static int
>  nftnl_expr_xfrm_snprintf(char *buf, size_t remain,
>  			 uint32_t flags, const struct nftnl_expr *e)
> @@ -196,5 +213,6 @@ struct expr_ops expr_ops_xfrm = {
>  	.get		= nftnl_expr_xfrm_get,
>  	.parse		= nftnl_expr_xfrm_parse,
>  	.build		= nftnl_expr_xfrm_build,
> +	.byteorder	= nftnl_expr_xfrm_byteorder,
>  	.snprintf	= nftnl_expr_xfrm_snprintf,
>  };
> diff --git a/src/rule.c b/src/rule.c
> index 0bb1c2a0583c1..9f1caa6feb57e 100644
> --- a/src/rule.c
> +++ b/src/rule.c
> @@ -549,6 +549,7 @@ static int nftnl_rule_snprintf_default(char *buf, size_t remain,
>  				       const struct nftnl_rule *r,
>  				       uint32_t type, uint32_t flags)
>  {
> +	struct nftnl_byteorder_ctx ctx = {};
>  	struct nftnl_expr *expr;
>  	int ret, offset = 0, i;
>  	const char *sep = "";
> @@ -603,6 +604,12 @@ static int nftnl_rule_snprintf_default(char *buf, size_t remain,
>  	ret = snprintf(buf + offset, remain, "\n");
>  	SNPRINTF_BUFFER_SIZE(ret, remain, offset);
>
> +	list_for_each_entry(expr, &r->expr_list, head) {
> +		if (!expr->ops->byteorder)
> +			continue;
> +		expr->ops->byteorder(&ctx, expr);
> +	}
> +
>  	list_for_each_entry(expr, &r->expr_list, head) {
>  		ret = snprintf(buf + offset, remain, "  [ %s ", expr->ops->name);
>  		SNPRINTF_BUFFER_SIZE(ret, remain, offset);

How about this?

  @@ -604,6 +605,9 @@ static int nftnl_rule_snprintf_default(char *buf, size_t remain,
          SNPRINTF_BUFFER_SIZE(ret, remain, offset);

          list_for_each_entry(expr, &r->expr_list, head) {
  +               if (expr->ops->byteorder)
  +                       expr->ops->byteorder(&ctx, expr);
  +
                  ret = snprintf(buf + offset, remain, "  [ %s ", expr->ops->name);
                  SNPRINTF_BUFFER_SIZE(ret, remain, offset);

> diff --git a/src/set_elem.c b/src/set_elem.c
> index 12eadce1f8e0c..9b18f4def6c47 100644
> --- a/src/set_elem.c
> +++ b/src/set_elem.c
> @@ -708,7 +708,8 @@ int nftnl_set_elem_snprintf_default(char *buf, size_t remain,
>  	SNPRINTF_BUFFER_SIZE(ret, remain, offset);
>
>  	ret = nftnl_data_reg_snprintf(buf + offset, remain, &e->key,
> -				      DATA_F_NOPFX, DATA_VALUE);
> +				      DATA_F_NOPFX, DATA_VALUE,
> +				      NFTNL_BYTEORDER_UNKNOWN);
>  	SNPRINTF_BUFFER_SIZE(ret, remain, offset);
>
>  	if (e->flags & (1 << NFTNL_SET_ELEM_KEY_END)) {
> @@ -716,7 +717,8 @@ int nftnl_set_elem_snprintf_default(char *buf, size_t remain,
>  		SNPRINTF_BUFFER_SIZE(ret, remain, offset);
>
>  		ret = nftnl_data_reg_snprintf(buf + offset, remain, &e->key_end,
> -					      DATA_F_NOPFX, DATA_VALUE);
> +					      DATA_F_NOPFX, DATA_VALUE,
> +					      NFTNL_BYTEORDER_UNKNOWN);
>  		SNPRINTF_BUFFER_SIZE(ret, remain, offset);
>  	}
>
> @@ -727,7 +729,8 @@ int nftnl_set_elem_snprintf_default(char *buf, size_t remain,
>  		dregtype = DATA_VERDICT;
>
>  	ret = nftnl_data_reg_snprintf(buf + offset, remain, &e->data,
> -				      DATA_F_NOPFX, dregtype);
> +				      DATA_F_NOPFX, dregtype,
> +				      NFTNL_BYTEORDER_UNKNOWN);
>  	SNPRINTF_BUFFER_SIZE(ret, remain, offset);
>
>  	ret = snprintf(buf + offset, remain, "%u [end]", e->set_elem_flags);
> --
> 2.33.0
>
>

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

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

* Re: [libnftnl PATCH 3/7] set: Introduce NFTNL_SET_DESC_CONCAT_DATA
  2021-11-30 17:45     ` Phil Sutter
@ 2022-01-05 16:17       ` Pablo Neira Ayuso
  2022-01-06 13:10         ` Phil Sutter
  0 siblings, 1 reply; 14+ messages in thread
From: Pablo Neira Ayuso @ 2022-01-05 16:17 UTC (permalink / raw)
  To: Phil Sutter, netfilter-devel

Hi Phil,

Sorry for taking a while to catch up on this.

On Tue, Nov 30, 2021 at 06:45:58PM +0100, Phil Sutter wrote:
> Hi Pablo,
> 
> On Tue, Nov 30, 2021 at 02:46:58PM +0100, Pablo Neira Ayuso wrote:
> > On Wed, Nov 24, 2021 at 06:22:38PM +0100, Phil Sutter wrote:
> > > Analogous to NFTNL_SET_DESC_CONCAT, introduce a data structure
> > > describing individual data lengths of elements' concatenated data
> > > fields.
> > > 
> > > Signed-off-by: Phil Sutter <phil@nwl.cc>
> > > ---
> > >  include/libnftnl/set.h | 1 +
> > >  include/set.h          | 2 ++
> > >  src/set.c              | 8 ++++++++
> > >  3 files changed, 11 insertions(+)
> > > 
> > > diff --git a/include/libnftnl/set.h b/include/libnftnl/set.h
> > > index 1ffb6c415260d..958bbc9065f67 100644
> > > --- a/include/libnftnl/set.h
> > > +++ b/include/libnftnl/set.h
> > > @@ -33,6 +33,7 @@ enum nftnl_set_attr {
> > >  	NFTNL_SET_EXPR,
> > >  	NFTNL_SET_EXPRESSIONS,
> > >  	NFTNL_SET_DESC_BYTEORDER,
> > > +	NFTNL_SET_DESC_CONCAT_DATA,
> > 
> > This information is already encoded in NFTNL_SET_DATA_TYPE, the
> > datatypes that are defined in libnftables have an explicit byteorder
> > and length.
> 
> We don't define data types in libnftnl, merely expressions and (with
> your patch) those define what byteorder the source/destination registers
> are supposed to be.

OK.

> > For concatenation, this information is stored in 6 bits (see
> > TYPE_BITS). By parsing the NFTNL_SET_DATA_TYPE field you can extract
> > both types (and byteorders) of the set definition.
> 
> For this to work, I would have to duplicate nftables' enum datatypes and
> in addition to that add an array defining each type's byteorder. I had
> considered this once, but didn't like the amount of duplication.
> 
> > For the typeof case, where a generic datatype such as integer is used,
> > this information is stored in the SET_USERDATA area.
> 
> This does not work for concatenated elements, right? At least I see e.g.
> NFTNL_UDATA_SET_KEYBYTEORDER being set to set->key->byteorder, so that's
> just a single value, no?
> 
> > This update for libnftnl is adding a third way to describe the
> > datatypes in the set, right?
> 
> Well, it extends the logic around NFTNL_SET_DESC_CONCAT to non-interval
> sets and to maps (adding the same data for the target part).
> 
> Then there is the new NFTNL_SET_DESC_BYTEORDER which defines the
> byteorder of each part of the key (and value in maps).

I think it would be good to skip these new NFTNL_SET_DESC_* attributes
since they are not used to pass a description to the kernel to help it
select the best set type. So, instead of nftnl_set_elem_snprintf_desc(),
it should be possible to add:

int nftnl_set_elem_snprintf2(char *buf, size_t size,
                             const struct nftnl_set *s,
                             const struct nftnl_set_elem *e,
                             uint32_t type, uint32_t flags)

(or pick a better name if you come up with any other alternative).

So the nftnl_set object provides the byteorder notation to display the
set element accordingly.

This requires an extra conversion from struct set to struct nftnl_set
for the debug case, that should be fine (--debug is slow path anyway).
If this needs to be speed up later on, it should be possible to keep
the nftnl_set object around as context.

Then, there is already NFTNL_UDATA_SET_KEYBYTEORDER and
NFTNL_UDATA_SET_DATABYTEORDER which store the byteorder for key and
data. I'm going to have a look at the userdata API since to see if I
can propose an easy API to set and to get userdata information (this
is currently a blob using TLVs that are stored in the kernel, but they
are not interpreted by the kernel, it's only useful context
information for userspace which is included in netlink dumps). This
should fill missing gap in my proposal.

Looking at your series, I think it's better it's better to avoid the
struct nftnl_set_desc definition that is exposed in the libnftnl
header, this will not allow for future extensions without breaking
binary compatibility. I understand your motivation is to avoid a
duplicated definition in the libnftnl and nftables codebase.

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

* Re: [libnftnl PATCH 3/7] set: Introduce NFTNL_SET_DESC_CONCAT_DATA
  2022-01-05 16:17       ` Pablo Neira Ayuso
@ 2022-01-06 13:10         ` Phil Sutter
  2022-01-20  0:08           ` Pablo Neira Ayuso
  0 siblings, 1 reply; 14+ messages in thread
From: Phil Sutter @ 2022-01-06 13:10 UTC (permalink / raw)
  To: Pablo Neira Ayuso; +Cc: netfilter-devel

Hey Pablo,

On Wed, Jan 05, 2022 at 05:17:43PM +0100, Pablo Neira Ayuso wrote:
> Sorry for taking a while to catch up on this.

No worries, thanks for looking into it.

> On Tue, Nov 30, 2021 at 06:45:58PM +0100, Phil Sutter wrote:
> > Hi Pablo,
> > 
> > On Tue, Nov 30, 2021 at 02:46:58PM +0100, Pablo Neira Ayuso wrote:
> > > On Wed, Nov 24, 2021 at 06:22:38PM +0100, Phil Sutter wrote:
> > > > Analogous to NFTNL_SET_DESC_CONCAT, introduce a data structure
> > > > describing individual data lengths of elements' concatenated data
> > > > fields.
> > > > 
> > > > Signed-off-by: Phil Sutter <phil@nwl.cc>
> > > > ---
> > > >  include/libnftnl/set.h | 1 +
> > > >  include/set.h          | 2 ++
> > > >  src/set.c              | 8 ++++++++
> > > >  3 files changed, 11 insertions(+)
> > > > 
> > > > diff --git a/include/libnftnl/set.h b/include/libnftnl/set.h
> > > > index 1ffb6c415260d..958bbc9065f67 100644
> > > > --- a/include/libnftnl/set.h
> > > > +++ b/include/libnftnl/set.h
> > > > @@ -33,6 +33,7 @@ enum nftnl_set_attr {
> > > >  	NFTNL_SET_EXPR,
> > > >  	NFTNL_SET_EXPRESSIONS,
> > > >  	NFTNL_SET_DESC_BYTEORDER,
> > > > +	NFTNL_SET_DESC_CONCAT_DATA,
> > > 
> > > This information is already encoded in NFTNL_SET_DATA_TYPE, the
> > > datatypes that are defined in libnftables have an explicit byteorder
> > > and length.
> > 
> > We don't define data types in libnftnl, merely expressions and (with
> > your patch) those define what byteorder the source/destination registers
> > are supposed to be.
> 
> OK.
> 
> > > For concatenation, this information is stored in 6 bits (see
> > > TYPE_BITS). By parsing the NFTNL_SET_DATA_TYPE field you can extract
> > > both types (and byteorders) of the set definition.
> > 
> > For this to work, I would have to duplicate nftables' enum datatypes and
> > in addition to that add an array defining each type's byteorder. I had
> > considered this once, but didn't like the amount of duplication.
> > 
> > > For the typeof case, where a generic datatype such as integer is used,
> > > this information is stored in the SET_USERDATA area.
> > 
> > This does not work for concatenated elements, right? At least I see e.g.
> > NFTNL_UDATA_SET_KEYBYTEORDER being set to set->key->byteorder, so that's
> > just a single value, no?
> > 
> > > This update for libnftnl is adding a third way to describe the
> > > datatypes in the set, right?
> > 
> > Well, it extends the logic around NFTNL_SET_DESC_CONCAT to non-interval
> > sets and to maps (adding the same data for the target part).
> > 
> > Then there is the new NFTNL_SET_DESC_BYTEORDER which defines the
> > byteorder of each part of the key (and value in maps).
> 
> I think it would be good to skip these new NFTNL_SET_DESC_* attributes
> since they are not used to pass a description to the kernel to help it
> select the best set type. So, instead of nftnl_set_elem_snprintf_desc(),
> it should be possible to add:
> 
> int nftnl_set_elem_snprintf2(char *buf, size_t size,
>                              const struct nftnl_set *s,
>                              const struct nftnl_set_elem *e,
>                              uint32_t type, uint32_t flags)
> 
> (or pick a better name if you come up with any other alternative).
> 
> So the nftnl_set object provides the byteorder notation to display the
> set element accordingly.

This is not sufficient per se - my series adds attributes to nftnl_set
to cover that. Passing the set itself is doable, I chose the
nftnl_set_desc way as it appeared a bit cleaner to me.

> This requires an extra conversion from struct set to struct nftnl_set
> for the debug case, that should be fine (--debug is slow path anyway).
> If this needs to be speed up later on, it should be possible to keep
> the nftnl_set object around as context.
> 
> Then, there is already NFTNL_UDATA_SET_KEYBYTEORDER and
> NFTNL_UDATA_SET_DATABYTEORDER which store the byteorder for key and
> data. I'm going to have a look at the userdata API since to see if I
> can propose an easy API to set and to get userdata information (this
> is currently a blob using TLVs that are stored in the kernel, but they
> are not interpreted by the kernel, it's only useful context
> information for userspace which is included in netlink dumps). This
> should fill missing gap in my proposal.

The two attributes hold a single byteorder value each. In order to
correctly print set elements, we need a value for each part of
concatenated elements. So for both key and data (key_end is always
identical to key), we need concat part length and byteorder.

> Looking at your series, I think it's better it's better to avoid the
> struct nftnl_set_desc definition that is exposed in the libnftnl
> header, this will not allow for future extensions without breaking
> binary compatibility. I understand your motivation is to avoid a
> duplicated definition in the libnftnl and nftables codebase.

I could introduce userdata attributes for the extra info to make things
more flexible, but it would bloat the data in kernel. OTOH this would
fix reverse path, when fetching data from kernel libnftnl lacks the
extra info to correctly dump the elements.

Cheers, Phil

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

* Re: [libnftnl PATCH 3/7] set: Introduce NFTNL_SET_DESC_CONCAT_DATA
  2022-01-06 13:10         ` Phil Sutter
@ 2022-01-20  0:08           ` Pablo Neira Ayuso
  0 siblings, 0 replies; 14+ messages in thread
From: Pablo Neira Ayuso @ 2022-01-20  0:08 UTC (permalink / raw)
  To: Phil Sutter, netfilter-devel

Hi Phil,

On Thu, Jan 06, 2022 at 02:10:58PM +0100, Phil Sutter wrote:
> Hey Pablo,
> 
> On Wed, Jan 05, 2022 at 05:17:43PM +0100, Pablo Neira Ayuso wrote:
> > Sorry for taking a while to catch up on this.
> 
> No worries, thanks for looking into it.
> 
> > On Tue, Nov 30, 2021 at 06:45:58PM +0100, Phil Sutter wrote:
> > > Hi Pablo,
> > > 
> > > On Tue, Nov 30, 2021 at 02:46:58PM +0100, Pablo Neira Ayuso wrote:
> > > > On Wed, Nov 24, 2021 at 06:22:38PM +0100, Phil Sutter wrote:
> > > > > Analogous to NFTNL_SET_DESC_CONCAT, introduce a data structure
> > > > > describing individual data lengths of elements' concatenated data
> > > > > fields.
> > > > > 
> > > > > Signed-off-by: Phil Sutter <phil@nwl.cc>
> > > > > ---
> > > > >  include/libnftnl/set.h | 1 +
> > > > >  include/set.h          | 2 ++
> > > > >  src/set.c              | 8 ++++++++
> > > > >  3 files changed, 11 insertions(+)
> > > > > 
> > > > > diff --git a/include/libnftnl/set.h b/include/libnftnl/set.h
> > > > > index 1ffb6c415260d..958bbc9065f67 100644
> > > > > --- a/include/libnftnl/set.h
> > > > > +++ b/include/libnftnl/set.h
> > > > > @@ -33,6 +33,7 @@ enum nftnl_set_attr {
> > > > >  	NFTNL_SET_EXPR,
> > > > >  	NFTNL_SET_EXPRESSIONS,
> > > > >  	NFTNL_SET_DESC_BYTEORDER,
> > > > > +	NFTNL_SET_DESC_CONCAT_DATA,
> > > > 
> > > > This information is already encoded in NFTNL_SET_DATA_TYPE, the
> > > > datatypes that are defined in libnftables have an explicit byteorder
> > > > and length.
> > > 
> > > We don't define data types in libnftnl, merely expressions and (with
> > > your patch) those define what byteorder the source/destination registers
> > > are supposed to be.
> > 
> > OK.
> > 
> > > > For concatenation, this information is stored in 6 bits (see
> > > > TYPE_BITS). By parsing the NFTNL_SET_DATA_TYPE field you can extract
> > > > both types (and byteorders) of the set definition.
> > > 
> > > For this to work, I would have to duplicate nftables' enum datatypes and
> > > in addition to that add an array defining each type's byteorder. I had
> > > considered this once, but didn't like the amount of duplication.
> > > 
> > > > For the typeof case, where a generic datatype such as integer is used,
> > > > this information is stored in the SET_USERDATA area.
> > > 
> > > This does not work for concatenated elements, right? At least I see e.g.
> > > NFTNL_UDATA_SET_KEYBYTEORDER being set to set->key->byteorder, so that's
> > > just a single value, no?
> > > 
> > > > This update for libnftnl is adding a third way to describe the
> > > > datatypes in the set, right?
> > > 
> > > Well, it extends the logic around NFTNL_SET_DESC_CONCAT to non-interval
> > > sets and to maps (adding the same data for the target part).
> > > 
> > > Then there is the new NFTNL_SET_DESC_BYTEORDER which defines the
> > > byteorder of each part of the key (and value in maps).
> > 
> > I think it would be good to skip these new NFTNL_SET_DESC_* attributes
> > since they are not used to pass a description to the kernel to help it
> > select the best set type. So, instead of nftnl_set_elem_snprintf_desc(),
> > it should be possible to add:
> > 
> > int nftnl_set_elem_snprintf2(char *buf, size_t size,
> >                              const struct nftnl_set *s,
> >                              const struct nftnl_set_elem *e,
> >                              uint32_t type, uint32_t flags)
> > 
> > (or pick a better name if you come up with any other alternative).
> > 
> > So the nftnl_set object provides the byteorder notation to display the
> > set element accordingly.
> 
> This is not sufficient per se - my series adds attributes to nftnl_set
> to cover that. Passing the set itself is doable, I chose the
> nftnl_set_desc way as it appeared a bit cleaner to me.

I agree some sort of description is needed.

> > This requires an extra conversion from struct set to struct nftnl_set
> > for the debug case, that should be fine (--debug is slow path anyway).
> > If this needs to be speed up later on, it should be possible to keep
> > the nftnl_set object around as context.
> > 
> > Then, there is already NFTNL_UDATA_SET_KEYBYTEORDER and
> > NFTNL_UDATA_SET_DATABYTEORDER which store the byteorder for key and
> > data. I'm going to have a look at the userdata API since to see if I
> > can propose an easy API to set and to get userdata information (this
> > is currently a blob using TLVs that are stored in the kernel, but they
> > are not interpreted by the kernel, it's only useful context
> > information for userspace which is included in netlink dumps). This
> > should fill missing gap in my proposal.
> 
> The two attributes hold a single byteorder value each. In order to
> correctly print set elements, we need a value for each part of
> concatenated elements. So for both key and data (key_end is always
> identical to key), we need concat part length and byteorder.

Yes, NFTNL_UDATA_SET_KEYBYTEORDER and NFTNL_UDATA_SET_DATABYTEORDER do
not support for concatenations.

There is also NFTA_SET_DATA_TYPE which should be deprecated in favour
if the userdata area.

> > Looking at your series, I think it's better it's better to avoid the
> > struct nftnl_set_desc definition that is exposed in the libnftnl
> > header, this will not allow for future extensions without breaking
> > binary compatibility. I understand your motivation is to avoid a
> > duplicated definition in the libnftnl and nftables codebase.
> 
> I could introduce userdata attributes for the extra info to make things
> more flexible, but it would bloat the data in kernel. OTOH this would
> fix reverse path, when fetching data from kernel libnftnl lacks the
> extra info to correctly dump the elements.

There is a need to store the byteorder and length for integer types,
we agreed to not add integer_u{8,16,32,64} and integer_be{8,16,32,64}
to promote typeof to declare sets. This needs to be store in the
userdata area so the userspace set listing path have context to
interpret the netlink dump from the kernel.

I have scratch a bit of time to bootstrap this series:

https://patchwork.ozlabs.org/project/netfilter-devel/list/?series=281902

Let me know, thanks for your patience.

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

end of thread, other threads:[~2022-01-20  0:09 UTC | newest]

Thread overview: 14+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2021-11-24 17:22 [libnftnl PATCH 0/7] Stabilize debug output on different endian systems Phil Sutter
2021-11-24 17:22 ` [libnftnl PATCH v2 1/7] src: add infrastructure to infer byteorder from keys Phil Sutter
2021-12-12 17:12   ` Jeremy Sowden
2021-11-24 17:22 ` [libnftnl PATCH 2/7] set: Introduce NFTNL_SET_DESC_BYTEORDER Phil Sutter
2021-11-24 17:22 ` [libnftnl PATCH 3/7] set: Introduce NFTNL_SET_DESC_CONCAT_DATA Phil Sutter
2021-11-30 13:46   ` Pablo Neira Ayuso
2021-11-30 17:45     ` Phil Sutter
2022-01-05 16:17       ` Pablo Neira Ayuso
2022-01-06 13:10         ` Phil Sutter
2022-01-20  0:08           ` Pablo Neira Ayuso
2021-11-24 17:22 ` [libnftnl PATCH 4/7] data_reg: Support varying byteorder in concat data Phil Sutter
2021-11-24 17:22 ` [libnftnl PATCH 5/7] data_reg: Respect each value's size Phil Sutter
2021-11-24 17:22 ` [libnftnl PATCH 6/7] include: Introduce and publish struct nftnl_set_desc Phil Sutter
2021-11-24 17:22 ` [libnftnl PATCH 7/7] set: Introduce nftnl_set_elem_snprintf_desc() Phil Sutter

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.