netfilter-devel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH iptables-nft 0/7] iptables: prefer native expressions for udp and tcp matches
@ 2022-01-25 16:52 Florian Westphal
  2022-01-25 16:52 ` [PATCH iptables-nft 1/7] nft-shared: support native tcp port delinearize Florian Westphal
                   ` (7 more replies)
  0 siblings, 8 replies; 9+ messages in thread
From: Florian Westphal @ 2022-01-25 16:52 UTC (permalink / raw)
  To: netfilter-devel; +Cc: Florian Westphal

This series switches iptables-nft to use native nft expressions
(payload, cmp, range, bitwise) to match on ports and tcp flags.

Patches are split up to first add delinearization support and
then switch the add/insert side over to generating those expressions.

Florian Westphal (7):
  nft-shared: support native tcp port delinearize
  nft-shared: support native tcp port range delinearize
  nft-shared: support native udp port delinearize
  nft: prefer native expressions instead of udp match
  nft: prefer native expressions instead of tcp match
  nft-shared: add tcp flag dissection
  nft: add support for native tcp flag matching

 iptables/nft-shared.c | 436 +++++++++++++++++++++++++++++++++++++++++-
 iptables/nft-shared.h |   5 +
 iptables/nft.c        | 182 ++++++++++++++++++
 3 files changed, 621 insertions(+), 2 deletions(-)

-- 
2.34.1


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

* [PATCH iptables-nft 1/7] nft-shared: support native tcp port delinearize
  2022-01-25 16:52 [PATCH iptables-nft 0/7] iptables: prefer native expressions for udp and tcp matches Florian Westphal
@ 2022-01-25 16:52 ` Florian Westphal
  2022-01-25 16:52 ` [PATCH iptables-nft 2/7] nft-shared: support native tcp port range delinearize Florian Westphal
                   ` (6 subsequent siblings)
  7 siblings, 0 replies; 9+ messages in thread
From: Florian Westphal @ 2022-01-25 16:52 UTC (permalink / raw)
  To: netfilter-devel; +Cc: Florian Westphal

This extends iptables-nft dissector to decode native tcp
port matching.  nft ruleset:

table ip filter {
        chain INPUT {
                type filter hook input priority filter; policy accept;
                tcp sport 12345
                tcp sport 12345 tcp dport 6789
                tcp sport < 1024
                tcp dport >= 1024
        }
}

$ iptables-nft-save
-A INPUT -p tcp -m tcp --sport 12345
-A INPUT -p tcp -m tcp --sport 12345 --dport 6789
-A INPUT -p tcp -m tcp --sport 0:1023
-A INPUT -p tcp -m tcp --dport 1024:65535

This would allow to extend iptables-nft to prefer
native payload expressions for --sport,dport in the future.

Also, parse_cmp must not clear the "payload" flag, this is because
cmp-based range expressions will contain following sequence:

payload => reg1
cmp reg1 > minv
cmp reg1 < maxv

... so second cmp would work.

Signed-off-by: Florian Westphal <fw@strlen.de>
---
 iptables/nft-shared.c | 178 +++++++++++++++++++++++++++++++++++++++++-
 iptables/nft-shared.h |   3 +
 2 files changed, 179 insertions(+), 2 deletions(-)

diff --git a/iptables/nft-shared.c b/iptables/nft-shared.c
index d0d0d558dde4..674c72b35ae7 100644
--- a/iptables/nft-shared.c
+++ b/iptables/nft-shared.c
@@ -468,6 +468,170 @@ static void nft_parse_bitwise(struct nft_xt_ctx *ctx, struct nftnl_expr *e)
 	ctx->flags |= NFT_XT_CTX_BITWISE;
 }
 
+static struct xtables_match *
+nft_create_match(struct nft_xt_ctx *ctx,
+		 struct iptables_command_state *cs,
+		 const char *name)
+{
+	struct xtables_match *match;
+	struct xt_entry_match *m;
+	unsigned int size;
+
+	match = xtables_find_match(name, XTF_TRY_LOAD,
+				   &cs->matches);
+	if (!match)
+		return NULL;
+
+	size = XT_ALIGN(sizeof(struct xt_entry_match)) + match->size;
+	m = xtables_calloc(1, size);
+	m->u.match_size = size;
+	m->u.user.revision = match->revision;
+
+	strcpy(m->u.user.name, match->name);
+	match->m = m;
+
+	xs_init_match(match);
+
+	return match;
+}
+
+static struct xt_tcp *nft_tcp_match(struct nft_xt_ctx *ctx,
+			            struct iptables_command_state *cs)
+{
+	struct xt_tcp *tcp = ctx->tcpudp.tcp;
+	struct xtables_match *match;
+
+	if (!tcp) {
+		match = nft_create_match(ctx, cs, "tcp");
+		if (!match)
+			return NULL;
+
+		tcp = (void*)match->m->data;
+		ctx->tcpudp.tcp = tcp;
+	}
+
+	return tcp;
+}
+
+static void nft_complete_tcp(struct nft_xt_ctx *ctx,
+			     struct iptables_command_state *cs,
+			     int sport, int dport,
+			     uint8_t op)
+{
+	struct xt_tcp *tcp = nft_tcp_match(ctx, cs);
+
+	if (!tcp)
+		return;
+
+	if (sport >= 0) {
+		switch (op) {
+		case NFT_CMP_NEQ:
+			tcp->invflags |= XT_TCP_INV_SRCPT;
+			/* fallthrough */
+		case NFT_CMP_EQ:
+			tcp->spts[0] = sport;
+			tcp->spts[1] = sport;
+			break;
+		case NFT_CMP_LT:
+			tcp->spts[1] = sport > 1 ? sport - 1 : 1;
+			break;
+		case NFT_CMP_LTE:
+			tcp->spts[1] = sport;
+			break;
+		case NFT_CMP_GT:
+			tcp->spts[0] = sport < 0xffff ? sport + 1 : 0xffff;
+			break;
+		case NFT_CMP_GTE:
+			tcp->spts[0] = sport;
+			break;
+		}
+	}
+
+	if (dport >= 0) {
+		switch (op) {
+		case NFT_CMP_NEQ:
+			tcp->invflags |= XT_TCP_INV_DSTPT;
+			/* fallthrough */
+		case NFT_CMP_EQ:
+			tcp->dpts[0] = dport;
+			tcp->dpts[1] = dport;
+			break;
+		case NFT_CMP_LT:
+			tcp->dpts[1] = dport > 1 ? dport - 1 : 1;
+			break;
+		case NFT_CMP_LTE:
+			tcp->dpts[1] = dport;
+			break;
+		case NFT_CMP_GT:
+			tcp->dpts[0] = dport < 0xffff ? dport + 1 : 0xffff;
+			break;
+		case NFT_CMP_GTE:
+			tcp->dpts[0] = dport;
+			break;
+		}
+	}
+}
+
+static void nft_complete_th_port(struct nft_xt_ctx *ctx,
+				 struct iptables_command_state *cs,
+				 uint8_t proto,
+				 int sport, int dport, uint8_t op)
+{
+	switch (proto) {
+	case IPPROTO_TCP:
+		nft_complete_tcp(ctx, cs, sport, dport, op);
+		break;
+	}
+}
+
+static void nft_complete_transport(struct nft_xt_ctx *ctx,
+				   struct nftnl_expr *e, void *data)
+{
+	struct iptables_command_state *cs = data;
+	uint32_t sdport;
+	uint16_t port;
+	uint8_t proto, op;
+	unsigned int len;
+
+	switch (ctx->h->family) {
+	case NFPROTO_IPV4:
+		proto = ctx->cs->fw.ip.proto;
+		break;
+	case NFPROTO_IPV6:
+		proto = ctx->cs->fw6.ipv6.proto;
+		break;
+	default:
+		proto = 0;
+		break;
+	}
+
+	nftnl_expr_get(e, NFTNL_EXPR_CMP_DATA, &len);
+	op = nftnl_expr_get_u32(e, NFTNL_EXPR_CMP_OP);
+
+	switch(ctx->payload.offset) {
+	case 0: /* th->sport */
+		switch (len) {
+		case 2: /* load sport only */
+			port = ntohs(nftnl_expr_get_u16(e, NFTNL_EXPR_CMP_DATA));
+			nft_complete_th_port(ctx, cs, proto, port, -1, op);
+			return;
+		case 4: /* load both src and dst port */
+			sdport = ntohl(nftnl_expr_get_u32(e, NFTNL_EXPR_CMP_DATA));
+			nft_complete_th_port(ctx, cs, proto, sdport >> 16, sdport & 0xffff, op);
+			return;
+		}
+		break;
+	case 2: /* th->dport */
+		switch (len) {
+		case 2:
+			port = ntohs(nftnl_expr_get_u16(e, NFTNL_EXPR_CMP_DATA));
+			nft_complete_th_port(ctx, cs, proto, -1, port, op);
+			return;
+		}
+		break;
+	}
+}
+
 static void nft_parse_cmp(struct nft_xt_ctx *ctx, struct nftnl_expr *e)
 {
 	void *data = ctx->cs;
@@ -483,8 +647,18 @@ static void nft_parse_cmp(struct nft_xt_ctx *ctx, struct nftnl_expr *e)
 	}
 	/* bitwise context is interpreted from payload */
 	if (ctx->flags & NFT_XT_CTX_PAYLOAD) {
-		ctx->h->ops->parse_payload(ctx, e, data);
-		ctx->flags &= ~NFT_XT_CTX_PAYLOAD;
+		switch (ctx->payload.base) {
+		case NFT_PAYLOAD_LL_HEADER:
+			if (ctx->h->family == NFPROTO_BRIDGE)
+				ctx->h->ops->parse_payload(ctx, e, data);
+			break;
+		case NFT_PAYLOAD_NETWORK_HEADER:
+			ctx->h->ops->parse_payload(ctx, e, data);
+			break;
+		case NFT_PAYLOAD_TRANSPORT_HEADER:
+			nft_complete_transport(ctx, e, data);
+			break;
+		}
 	}
 }
 
diff --git a/iptables/nft-shared.h b/iptables/nft-shared.h
index c3241f4b8c72..0a8be7099aa2 100644
--- a/iptables/nft-shared.h
+++ b/iptables/nft-shared.h
@@ -53,6 +53,9 @@ struct nft_xt_ctx {
 	struct nft_handle *h;
 	uint32_t flags;
 	const char *table;
+	union {
+		struct xt_tcp *tcp;
+	} tcpudp;
 
 	uint32_t reg;
 	struct {
-- 
2.34.1


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

* [PATCH iptables-nft 2/7] nft-shared: support native tcp port range delinearize
  2022-01-25 16:52 [PATCH iptables-nft 0/7] iptables: prefer native expressions for udp and tcp matches Florian Westphal
  2022-01-25 16:52 ` [PATCH iptables-nft 1/7] nft-shared: support native tcp port delinearize Florian Westphal
@ 2022-01-25 16:52 ` Florian Westphal
  2022-01-25 16:52 ` [PATCH iptables-nft 3/7] nft-shared: support native udp port delinearize Florian Westphal
                   ` (5 subsequent siblings)
  7 siblings, 0 replies; 9+ messages in thread
From: Florian Westphal @ 2022-01-25 16:52 UTC (permalink / raw)
  To: netfilter-devel; +Cc: Florian Westphal

adds support for
nft ... tcp dport != min-max

Signed-off-by: Florian Westphal <fw@strlen.de>
---
 iptables/nft-shared.c | 113 ++++++++++++++++++++++++++++++++++++++++++
 iptables/nft-shared.h |   1 +
 iptables/nft.c        |   1 +
 3 files changed, 115 insertions(+)

diff --git a/iptables/nft-shared.c b/iptables/nft-shared.c
index 674c72b35ae7..7f6b4ff392d9 100644
--- a/iptables/nft-shared.c
+++ b/iptables/nft-shared.c
@@ -513,6 +513,43 @@ static struct xt_tcp *nft_tcp_match(struct nft_xt_ctx *ctx,
 	return tcp;
 }
 
+static void nft_complete_tcp_range(struct nft_xt_ctx *ctx,
+			           struct iptables_command_state *cs,
+			           int sport_from, int sport_to,
+			           int dport_from, int dport_to,
+				   uint8_t op)
+{
+	struct xt_tcp *tcp = nft_tcp_match(ctx, cs);
+
+	if (!tcp)
+		return;
+
+	if (sport_from >= 0) {
+		switch (op) {
+		case NFT_RANGE_NEQ:
+			tcp->invflags |= XT_TCP_INV_SRCPT;
+			/* fallthrough */
+		case NFT_RANGE_EQ:
+			tcp->spts[0] = sport_from;
+			tcp->spts[1] = sport_to;
+			break;
+		}
+	}
+
+	if (dport_to >= 0) {
+		switch (op) {
+		case NFT_CMP_NEQ:
+			tcp->invflags |= XT_TCP_INV_DSTPT;
+			/* fallthrough */
+		case NFT_CMP_EQ:
+			tcp->dpts[0] = dport_from;
+			tcp->dpts[1] = dport_to;
+			break;
+		}
+	}
+}
+
+
 static void nft_complete_tcp(struct nft_xt_ctx *ctx,
 			     struct iptables_command_state *cs,
 			     int sport, int dport,
@@ -584,6 +621,20 @@ static void nft_complete_th_port(struct nft_xt_ctx *ctx,
 	}
 }
 
+static void nft_complete_th_port_range(struct nft_xt_ctx *ctx,
+				       struct iptables_command_state *cs,
+				       uint8_t proto,
+				       int sport_from, int sport_to,
+				       int dport_from, int dport_to, uint8_t op)
+{
+	switch (proto) {
+	case IPPROTO_TCP:
+		nft_complete_tcp_range(ctx, cs, sport_from, sport_to, dport_from, dport_to, op);
+		break;
+	}
+}
+
+
 static void nft_complete_transport(struct nft_xt_ctx *ctx,
 				   struct nftnl_expr *e, void *data)
 {
@@ -632,6 +683,47 @@ static void nft_complete_transport(struct nft_xt_ctx *ctx,
 	}
 }
 
+static void nft_complete_transport_range(struct nft_xt_ctx *ctx,
+					 struct nftnl_expr *e,
+					 struct iptables_command_state *cs)
+{
+	unsigned int len_from, len_to;
+	uint8_t proto, op;
+	uint16_t from, to;
+
+	switch (ctx->h->family) {
+	case NFPROTO_IPV4:
+		proto = ctx->cs->fw.ip.proto;
+		break;
+	case NFPROTO_IPV6:
+		proto = ctx->cs->fw6.ipv6.proto;
+		break;
+	default:
+		proto = 0;
+		break;
+	}
+
+	nftnl_expr_get(e, NFTNL_EXPR_RANGE_FROM_DATA, &len_from);
+	nftnl_expr_get(e, NFTNL_EXPR_RANGE_FROM_DATA, &len_to);
+	if (len_to != len_from || len_to != 2)
+		return;
+
+	op = nftnl_expr_get_u32(e, NFTNL_EXPR_RANGE_OP);
+
+	from = ntohs(nftnl_expr_get_u16(e, NFTNL_EXPR_RANGE_FROM_DATA));
+	to = ntohs(nftnl_expr_get_u16(e, NFTNL_EXPR_RANGE_TO_DATA));
+
+	switch(ctx->payload.offset) {
+	case 0:
+		nft_complete_th_port_range(ctx, cs, proto, from, to, -1, -1, op);
+		return;
+	case 2:
+		to = ntohs(nftnl_expr_get_u16(e, NFTNL_EXPR_RANGE_TO_DATA));
+		nft_complete_th_port_range(ctx, cs, proto, -1, -1, from, to, op);
+		return;
+	}
+}
+
 static void nft_parse_cmp(struct nft_xt_ctx *ctx, struct nftnl_expr *e)
 {
 	void *data = ctx->cs;
@@ -811,6 +903,25 @@ static void nft_parse_lookup(struct nft_xt_ctx *ctx, struct nft_handle *h,
 		ctx->h->ops->parse_lookup(ctx, e, NULL);
 }
 
+static void nft_parse_range(struct nft_xt_ctx *ctx, struct nftnl_expr *e)
+{
+	uint32_t reg;
+
+	reg = nftnl_expr_get_u32(e, NFTNL_EXPR_RANGE_SREG);
+	if (reg != ctx->reg)
+		return;
+
+	if (ctx->flags & NFT_XT_CTX_PAYLOAD) {
+		switch (ctx->payload.base) {
+		case NFT_PAYLOAD_TRANSPORT_HEADER:
+			nft_complete_transport_range(ctx, e, ctx->cs);
+			break;
+		default:
+			break;
+		}
+	}
+}
+
 void nft_rule_to_iptables_command_state(struct nft_handle *h,
 					const struct nftnl_rule *r,
 					struct iptables_command_state *cs)
@@ -855,6 +966,8 @@ void nft_rule_to_iptables_command_state(struct nft_handle *h,
 			nft_parse_lookup(&ctx, h, expr);
 		else if (strcmp(name, "log") == 0)
 			nft_parse_log(&ctx, expr);
+		else if (strcmp(name, "range") == 0)
+			nft_parse_range(&ctx, expr);
 
 		expr = nftnl_expr_iter_next(iter);
 	}
diff --git a/iptables/nft-shared.h b/iptables/nft-shared.h
index 0a8be7099aa2..1468d5608158 100644
--- a/iptables/nft-shared.h
+++ b/iptables/nft-shared.h
@@ -45,6 +45,7 @@ enum {
 	NFT_XT_CTX_BITWISE	= (1 << 2),
 	NFT_XT_CTX_IMMEDIATE	= (1 << 3),
 	NFT_XT_CTX_PREV_PAYLOAD	= (1 << 4),
+	NFT_XT_CTX_RANGE	= (1 << 5),
 };
 
 struct nft_xt_ctx {
diff --git a/iptables/nft.c b/iptables/nft.c
index b5de687c5c4c..f7f5950625d0 100644
--- a/iptables/nft.c
+++ b/iptables/nft.c
@@ -3529,6 +3529,7 @@ static const char *supported_exprs[] = {
 	"counter",
 	"immediate",
 	"lookup",
+	"range",
 };
 
 
-- 
2.34.1


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

* [PATCH iptables-nft 3/7] nft-shared: support native udp port delinearize
  2022-01-25 16:52 [PATCH iptables-nft 0/7] iptables: prefer native expressions for udp and tcp matches Florian Westphal
  2022-01-25 16:52 ` [PATCH iptables-nft 1/7] nft-shared: support native tcp port delinearize Florian Westphal
  2022-01-25 16:52 ` [PATCH iptables-nft 2/7] nft-shared: support native tcp port range delinearize Florian Westphal
@ 2022-01-25 16:52 ` Florian Westphal
  2022-01-25 16:52 ` [PATCH iptables-nft 4/7] nft: prefer native expressions instead of udp match Florian Westphal
                   ` (4 subsequent siblings)
  7 siblings, 0 replies; 9+ messages in thread
From: Florian Westphal @ 2022-01-25 16:52 UTC (permalink / raw)
  To: netfilter-devel; +Cc: Florian Westphal

same as previous patch, but for udp.

Signed-off-by: Florian Westphal <fw@strlen.de>
---
 iptables/nft-shared.c | 117 ++++++++++++++++++++++++++++++++++++++++++
 iptables/nft-shared.h |   1 +
 2 files changed, 118 insertions(+)

diff --git a/iptables/nft-shared.c b/iptables/nft-shared.c
index 7f6b4ff392d9..19c82854f758 100644
--- a/iptables/nft-shared.c
+++ b/iptables/nft-shared.c
@@ -495,6 +495,24 @@ nft_create_match(struct nft_xt_ctx *ctx,
 	return match;
 }
 
+static struct xt_udp *nft_udp_match(struct nft_xt_ctx *ctx,
+			            struct iptables_command_state *cs)
+{
+	struct xt_udp *udp = ctx->tcpudp.udp;
+	struct xtables_match *match;
+
+	if (!udp) {
+		match = nft_create_match(ctx, cs, "udp");
+		if (!match)
+			return NULL;
+
+		udp = (void*)match->m->data;
+		ctx->tcpudp.udp = udp;
+	}
+
+	return udp;
+}
+
 static struct xt_tcp *nft_tcp_match(struct nft_xt_ctx *ctx,
 			            struct iptables_command_state *cs)
 {
@@ -513,6 +531,42 @@ static struct xt_tcp *nft_tcp_match(struct nft_xt_ctx *ctx,
 	return tcp;
 }
 
+static void nft_complete_udp_range(struct nft_xt_ctx *ctx,
+			           struct iptables_command_state *cs,
+			           int sport_from, int sport_to,
+			           int dport_from, int dport_to,
+				   uint8_t op)
+{
+	struct xt_udp *udp = nft_udp_match(ctx, cs);
+
+	if (!udp)
+		return;
+
+	if (sport_from >= 0) {
+		switch (op) {
+		case NFT_RANGE_NEQ:
+			udp->invflags |= XT_UDP_INV_SRCPT;
+			/* fallthrough */
+		case NFT_RANGE_EQ:
+			udp->spts[0] = sport_from;
+			udp->spts[1] = sport_to;
+			break;
+		}
+	}
+
+	if (dport_to >= 0) {
+		switch (op) {
+		case NFT_CMP_NEQ:
+			udp->invflags |= XT_UDP_INV_DSTPT;
+			/* fallthrough */
+		case NFT_CMP_EQ:
+			udp->dpts[0] = dport_from;
+			udp->dpts[1] = dport_to;
+			break;
+		}
+	}
+}
+
 static void nft_complete_tcp_range(struct nft_xt_ctx *ctx,
 			           struct iptables_command_state *cs,
 			           int sport_from, int sport_to,
@@ -549,6 +603,63 @@ static void nft_complete_tcp_range(struct nft_xt_ctx *ctx,
 	}
 }
 
+static void nft_complete_udp(struct nft_xt_ctx *ctx,
+			     struct iptables_command_state *cs,
+			     int sport, int dport,
+			     uint8_t op)
+{
+	struct xt_udp *udp = nft_udp_match(ctx, cs);
+
+	if (!udp)
+		return;
+
+	if (sport >= 0) {
+		switch (op) {
+		case NFT_CMP_NEQ:
+			udp->invflags |= XT_UDP_INV_SRCPT;
+			/* fallthrough */
+		case NFT_CMP_EQ:
+			udp->spts[0] = sport;
+			udp->spts[1] = sport;
+			break;
+		case NFT_CMP_LT:
+			udp->spts[1] = sport > 1 ? sport - 1 : 1;
+			break;
+		case NFT_CMP_LTE:
+			udp->spts[1] = sport;
+			break;
+		case NFT_CMP_GT:
+			udp->spts[0] = sport < 0xffff ? sport + 1 : 0xffff;
+			break;
+		case NFT_CMP_GTE:
+			udp->spts[0] = sport;
+			break;
+		}
+	}
+	if (dport >= 0) {
+		switch (op) {
+		case NFT_CMP_NEQ:
+			udp->invflags |= XT_UDP_INV_DSTPT;
+			/* fallthrough */
+		case NFT_CMP_EQ:
+			udp->dpts[0] = dport;
+			udp->dpts[1] = dport;
+			break;
+		case NFT_CMP_LT:
+			udp->dpts[1] = dport > 1 ? dport - 1 : 1;
+			break;
+		case NFT_CMP_LTE:
+			udp->dpts[1] = dport;
+			break;
+		case NFT_CMP_GT:
+			udp->dpts[0] = dport < 0xffff ? dport + 1 : 0xffff;
+			break;
+		case NFT_CMP_GTE:
+			udp->dpts[0] = dport;
+			break;
+		}
+	}
+}
 
 static void nft_complete_tcp(struct nft_xt_ctx *ctx,
 			     struct iptables_command_state *cs,
@@ -615,6 +726,9 @@ static void nft_complete_th_port(struct nft_xt_ctx *ctx,
 				 int sport, int dport, uint8_t op)
 {
 	switch (proto) {
+	case IPPROTO_UDP:
+		nft_complete_udp(ctx, cs, sport, dport, op);
+		break;
 	case IPPROTO_TCP:
 		nft_complete_tcp(ctx, cs, sport, dport, op);
 		break;
@@ -628,6 +742,9 @@ static void nft_complete_th_port_range(struct nft_xt_ctx *ctx,
 				       int dport_from, int dport_to, uint8_t op)
 {
 	switch (proto) {
+	case IPPROTO_UDP:
+		nft_complete_udp_range(ctx, cs, sport_from, sport_to, dport_from, dport_to, op);
+		break;
 	case IPPROTO_TCP:
 		nft_complete_tcp_range(ctx, cs, sport_from, sport_to, dport_from, dport_to, op);
 		break;
diff --git a/iptables/nft-shared.h b/iptables/nft-shared.h
index 1468d5608158..0716c8f4a509 100644
--- a/iptables/nft-shared.h
+++ b/iptables/nft-shared.h
@@ -56,6 +56,7 @@ struct nft_xt_ctx {
 	const char *table;
 	union {
 		struct xt_tcp *tcp;
+		struct xt_udp *udp;
 	} tcpudp;
 
 	uint32_t reg;
-- 
2.34.1


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

* [PATCH iptables-nft 4/7] nft: prefer native expressions instead of udp match
  2022-01-25 16:52 [PATCH iptables-nft 0/7] iptables: prefer native expressions for udp and tcp matches Florian Westphal
                   ` (2 preceding siblings ...)
  2022-01-25 16:52 ` [PATCH iptables-nft 3/7] nft-shared: support native udp port delinearize Florian Westphal
@ 2022-01-25 16:52 ` Florian Westphal
  2022-01-25 16:52 ` [PATCH iptables-nft 5/7] nft: prefer native expressions instead of tcp match Florian Westphal
                   ` (3 subsequent siblings)
  7 siblings, 0 replies; 9+ messages in thread
From: Florian Westphal @ 2022-01-25 16:52 UTC (permalink / raw)
  To: netfilter-devel; +Cc: Florian Westphal

Instead of using nft_compat+xtables udp match, prefer to
emit payload+cmp or payload+range expression.

Delinearization support was added in previous patches.

Signed-off-by: Florian Westphal <fw@strlen.de>
---
 iptables/nft.c | 122 +++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 122 insertions(+)

diff --git a/iptables/nft.c b/iptables/nft.c
index f7f5950625d0..9f181de53678 100644
--- a/iptables/nft.c
+++ b/iptables/nft.c
@@ -1226,6 +1226,126 @@ static int add_nft_among(struct nft_handle *h,
 	return 0;
 }
 
+static int expr_gen_range_cmp16(struct nftnl_rule *r,
+				uint16_t lo,
+				uint16_t hi,
+				bool invert)
+{
+	struct nftnl_expr *e;
+
+	if (lo == hi) {
+		add_cmp_u16(r, htons(lo), invert ? NFT_CMP_NEQ : NFT_CMP_EQ);
+		return 0;
+	}
+
+	if (lo == 0 && hi < 0xffff) {
+		add_cmp_u16(r, htons(hi) , invert ? NFT_CMP_GT : NFT_CMP_LTE);
+		return 0;
+	}
+
+	e = nftnl_expr_alloc("range");
+	if (!e)
+		return -ENOMEM;
+
+	nftnl_expr_set_u32(e, NFTNL_EXPR_RANGE_SREG, NFT_REG_1);
+	nftnl_expr_set_u32(e, NFTNL_EXPR_RANGE_OP, invert ? NFT_RANGE_NEQ : NFT_RANGE_EQ);
+
+	lo = htons(lo);
+	nftnl_expr_set(e, NFTNL_EXPR_RANGE_FROM_DATA, &lo, sizeof(lo));
+	hi = htons(hi);
+	nftnl_expr_set(e, NFTNL_EXPR_RANGE_TO_DATA, &hi, sizeof(hi));
+
+	nftnl_rule_add_expr(r, e);
+	return 0;
+}
+
+static int add_nft_tcpudp(struct nftnl_rule *r,
+			  uint16_t src[2],
+			  bool invert_src,
+			  uint16_t dst[2],
+			  bool invert_dst)
+{
+	struct nftnl_expr *expr;
+	uint8_t op = NFT_CMP_EQ;
+	int ret;
+
+	if (src[0] && src[0] == src[1] &&
+	    dst[0] && dst[0] == dst[1] &&
+	    invert_src == invert_dst) {
+		uint32_t combined = dst[0] | (src[0] << 16);
+
+		if (invert_src)
+			op = NFT_CMP_NEQ;
+
+		expr = gen_payload(NFT_PAYLOAD_TRANSPORT_HEADER, 0, 4,
+				   NFT_REG_1);
+		if (!expr)
+			return -ENOMEM;
+
+		nftnl_rule_add_expr(r, expr);
+		add_cmp_u32(r, htonl(combined), op);
+		return 0;
+	}
+
+	if (src[0] || src[1] < 0xffff) {
+		expr = gen_payload(NFT_PAYLOAD_TRANSPORT_HEADER,
+				   0, 2, NFT_REG_1);
+		if (!expr)
+			return -ENOMEM;
+
+		nftnl_rule_add_expr(r, expr);
+		ret = expr_gen_range_cmp16(r, src[0], src[1], invert_src);
+		if (ret)
+			return ret;
+	}
+
+	if (dst[0] || dst[1] < 0xffff) {
+		expr = gen_payload(NFT_PAYLOAD_TRANSPORT_HEADER,
+				   2, 2, NFT_REG_1);
+		if (!expr)
+			return -ENOMEM;
+
+		nftnl_rule_add_expr(r, expr);
+		ret = expr_gen_range_cmp16(r, dst[0], dst[1], invert_dst);
+		if (ret)
+			return ret;
+	}
+
+	return 0;
+}
+
+/* without this, "iptables -A INPUT -m udp" is
+ * turned into "iptables -A INPUT", which isn't
+ * compatible with iptables-legacy behaviour.
+ */
+static bool udp_all_zero(const struct xt_udp *u)
+{
+	static const struct xt_udp zero = {
+		.spts[1] = 0xffff,
+		.dpts[1] = 0xffff,
+	};
+
+	return memcmp(u, &zero, sizeof(*u)) == 0;
+}
+
+static int add_nft_udp(struct nftnl_rule *r, struct xt_entry_match *m)
+{
+	struct xt_udp *udp = (void *)m->data;
+
+	if (udp->invflags > XT_UDP_INV_MASK ||
+	    udp_all_zero(udp)) {
+		struct nftnl_expr *expr = nftnl_expr_alloc("match");
+		int ret;
+
+		ret = __add_match(expr, m);
+		nftnl_rule_add_expr(r, expr);
+		return ret;
+	}
+
+	return add_nft_tcpudp(r, udp->spts, udp->invflags & XT_UDP_INV_SRCPT,
+			      udp->dpts, udp->invflags & XT_UDP_INV_DSTPT);
+}
+
 int add_match(struct nft_handle *h,
 	      struct nftnl_rule *r, struct xt_entry_match *m)
 {
@@ -1236,6 +1356,8 @@ int add_match(struct nft_handle *h,
 		return add_nft_limit(r, m);
 	else if (!strcmp(m->u.user.name, "among"))
 		return add_nft_among(h, r, m);
+	else if (!strcmp(m->u.user.name, "udp"))
+		return add_nft_udp(r, m);
 
 	expr = nftnl_expr_alloc("match");
 	if (expr == NULL)
-- 
2.34.1


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

* [PATCH iptables-nft 5/7] nft: prefer native expressions instead of tcp match
  2022-01-25 16:52 [PATCH iptables-nft 0/7] iptables: prefer native expressions for udp and tcp matches Florian Westphal
                   ` (3 preceding siblings ...)
  2022-01-25 16:52 ` [PATCH iptables-nft 4/7] nft: prefer native expressions instead of udp match Florian Westphal
@ 2022-01-25 16:52 ` Florian Westphal
  2022-01-25 16:53 ` [PATCH iptables-nft 6/7] nft-shared: add tcp flag dissection Florian Westphal
                   ` (2 subsequent siblings)
  7 siblings, 0 replies; 9+ messages in thread
From: Florian Westphal @ 2022-01-25 16:52 UTC (permalink / raw)
  To: netfilter-devel; +Cc: Florian Westphal

Instead of using nft_compat+xtables tcp match, prefer to
emit payload+cmp or payload+range expression.

Unlike udp, tcp has flag bits that can be matched too but
we have to fall back to the xt expression for now.
We also don't support tcp option match, but thats a rarely
used feature anyway.

Delinearization support for ports was added in previous patches.

Signed-off-by: Florian Westphal <fw@strlen.de>
---
 iptables/nft.c | 32 ++++++++++++++++++++++++++++++++
 1 file changed, 32 insertions(+)

diff --git a/iptables/nft.c b/iptables/nft.c
index 9f181de53678..4b5c4332c7c1 100644
--- a/iptables/nft.c
+++ b/iptables/nft.c
@@ -1346,6 +1346,36 @@ static int add_nft_udp(struct nftnl_rule *r, struct xt_entry_match *m)
 			      udp->dpts, udp->invflags & XT_UDP_INV_DSTPT);
 }
 
+static bool tcp_all_zero(const struct xt_tcp *t)
+{
+	static const struct xt_tcp zero = {
+		.spts[1] = 0xffff,
+		.dpts[1] = 0xffff,
+	};
+
+	return memcmp(t, &zero, sizeof(*t)) == 0;
+}
+
+static int add_nft_tcp(struct nftnl_rule *r, struct xt_entry_match *m)
+{
+	static const uint8_t supported = XT_TCP_INV_SRCPT | XT_TCP_INV_DSTPT;
+	struct xt_tcp *tcp = (void *)m->data;
+
+	if (tcp->invflags & ~supported || tcp->option ||
+	    tcp->flg_mask || tcp->flg_cmp ||
+	    tcp_all_zero(tcp)) {
+		struct nftnl_expr *expr = nftnl_expr_alloc("match");
+		int ret;
+
+		ret = __add_match(expr, m);
+		nftnl_rule_add_expr(r, expr);
+		return ret;
+	}
+
+	return add_nft_tcpudp(r, tcp->spts, tcp->invflags & XT_TCP_INV_SRCPT,
+			      tcp->dpts, tcp->invflags & XT_TCP_INV_DSTPT);
+}
+
 int add_match(struct nft_handle *h,
 	      struct nftnl_rule *r, struct xt_entry_match *m)
 {
@@ -1358,6 +1388,8 @@ int add_match(struct nft_handle *h,
 		return add_nft_among(h, r, m);
 	else if (!strcmp(m->u.user.name, "udp"))
 		return add_nft_udp(r, m);
+	else if (!strcmp(m->u.user.name, "tcp"))
+		return add_nft_tcp(r, m);
 
 	expr = nftnl_expr_alloc("match");
 	if (expr == NULL)
-- 
2.34.1


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

* [PATCH iptables-nft 6/7] nft-shared: add tcp flag dissection
  2022-01-25 16:52 [PATCH iptables-nft 0/7] iptables: prefer native expressions for udp and tcp matches Florian Westphal
                   ` (4 preceding siblings ...)
  2022-01-25 16:52 ` [PATCH iptables-nft 5/7] nft: prefer native expressions instead of tcp match Florian Westphal
@ 2022-01-25 16:53 ` Florian Westphal
  2022-01-25 16:53 ` [PATCH iptables-nft 7/7] nft: add support for native tcp flag matching Florian Westphal
  2022-01-29 12:44 ` [PATCH iptables-nft 0/7] iptables: prefer native expressions for udp and tcp matches Florian Westphal
  7 siblings, 0 replies; 9+ messages in thread
From: Florian Westphal @ 2022-01-25 16:53 UTC (permalink / raw)
  To: netfilter-devel; +Cc: Florian Westphal

Detect payload load of th->flags and convert it to xt tcp match
structure.

Signed-off-by: Florian Westphal <fw@strlen.de>
---
 iptables/nft-shared.c | 28 ++++++++++++++++++++++++++++
 1 file changed, 28 insertions(+)

diff --git a/iptables/nft-shared.c b/iptables/nft-shared.c
index 19c82854f758..74f08c8966ee 100644
--- a/iptables/nft-shared.c
+++ b/iptables/nft-shared.c
@@ -751,6 +751,22 @@ static void nft_complete_th_port_range(struct nft_xt_ctx *ctx,
 	}
 }
 
+static void nft_complete_tcp_flags(struct nft_xt_ctx *ctx,
+				   struct iptables_command_state *cs,
+				   uint8_t op,
+				   uint8_t flags,
+				   uint8_t mask)
+{
+	struct xt_tcp *tcp = nft_tcp_match(ctx, cs);
+
+	if (!tcp)
+		return;
+
+	if (op == NFT_CMP_NEQ)
+		tcp->invflags |= XT_TCP_INV_FLAGS;
+	tcp->flg_cmp = flags;
+	tcp->flg_mask = mask;
+}
 
 static void nft_complete_transport(struct nft_xt_ctx *ctx,
 				   struct nftnl_expr *e, void *data)
@@ -797,6 +813,18 @@ static void nft_complete_transport(struct nft_xt_ctx *ctx,
 			return;
 		}
 		break;
+	case 13: /* th->flags */
+		if (len == 1 && proto == IPPROTO_TCP) {
+			uint8_t flags = nftnl_expr_get_u8(e, NFTNL_EXPR_CMP_DATA);
+			uint8_t mask = ~0;
+
+			if (ctx->flags & NFT_XT_CTX_BITWISE) {
+				memcpy(&mask, &ctx->bitwise.mask, sizeof(mask));
+				ctx->flags &= ~NFT_XT_CTX_BITWISE;
+			}
+			nft_complete_tcp_flags(ctx, cs, op, flags, mask);
+		}
+		return;
 	}
 }
 
-- 
2.34.1


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

* [PATCH iptables-nft 7/7] nft: add support for native tcp flag matching
  2022-01-25 16:52 [PATCH iptables-nft 0/7] iptables: prefer native expressions for udp and tcp matches Florian Westphal
                   ` (5 preceding siblings ...)
  2022-01-25 16:53 ` [PATCH iptables-nft 6/7] nft-shared: add tcp flag dissection Florian Westphal
@ 2022-01-25 16:53 ` Florian Westphal
  2022-01-29 12:44 ` [PATCH iptables-nft 0/7] iptables: prefer native expressions for udp and tcp matches Florian Westphal
  7 siblings, 0 replies; 9+ messages in thread
From: Florian Westphal @ 2022-01-25 16:53 UTC (permalink / raw)
  To: netfilter-devel; +Cc: Florian Westphal

prefer payload + bitwise + cmp to nft_compat match.

Signed-off-by: Florian Westphal <fw@strlen.de>
---
 iptables/nft.c | 31 +++++++++++++++++++++++++++++--
 1 file changed, 29 insertions(+), 2 deletions(-)

diff --git a/iptables/nft.c b/iptables/nft.c
index 4b5c4332c7c1..3e4345499794 100644
--- a/iptables/nft.c
+++ b/iptables/nft.c
@@ -1346,6 +1346,26 @@ static int add_nft_udp(struct nftnl_rule *r, struct xt_entry_match *m)
 			      udp->dpts, udp->invflags & XT_UDP_INV_DSTPT);
 }
 
+static int add_nft_tcpflags(struct nftnl_rule *r,
+			    uint8_t cmp, uint8_t mask,
+			    bool invert)
+{
+	struct nftnl_expr *e;
+
+	e = gen_payload(NFT_PAYLOAD_TRANSPORT_HEADER,
+			13, 1, NFT_REG_1);
+
+	if (!e)
+		return -ENOMEM;
+
+	nftnl_rule_add_expr(r, e);
+
+	add_bitwise(r, &mask, 1);
+	add_cmp_u8(r, cmp, invert ? NFT_CMP_NEQ : NFT_CMP_EQ);
+
+	return 0;
+}
+
 static bool tcp_all_zero(const struct xt_tcp *t)
 {
 	static const struct xt_tcp zero = {
@@ -1358,11 +1378,10 @@ static bool tcp_all_zero(const struct xt_tcp *t)
 
 static int add_nft_tcp(struct nftnl_rule *r, struct xt_entry_match *m)
 {
-	static const uint8_t supported = XT_TCP_INV_SRCPT | XT_TCP_INV_DSTPT;
+	static const uint8_t supported = XT_TCP_INV_SRCPT | XT_TCP_INV_DSTPT | XT_TCP_INV_FLAGS;
 	struct xt_tcp *tcp = (void *)m->data;
 
 	if (tcp->invflags & ~supported || tcp->option ||
-	    tcp->flg_mask || tcp->flg_cmp ||
 	    tcp_all_zero(tcp)) {
 		struct nftnl_expr *expr = nftnl_expr_alloc("match");
 		int ret;
@@ -1372,6 +1391,14 @@ static int add_nft_tcp(struct nftnl_rule *r, struct xt_entry_match *m)
 		return ret;
 	}
 
+	if (tcp->flg_mask) {
+		int ret = add_nft_tcpflags(r, tcp->flg_cmp, tcp->flg_mask,
+					   tcp->invflags & XT_TCP_INV_FLAGS);
+
+		if (ret < 0)
+			return ret;
+	}
+
 	return add_nft_tcpudp(r, tcp->spts, tcp->invflags & XT_TCP_INV_SRCPT,
 			      tcp->dpts, tcp->invflags & XT_TCP_INV_DSTPT);
 }
-- 
2.34.1


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

* Re: [PATCH iptables-nft 0/7] iptables: prefer native expressions for udp and tcp matches
  2022-01-25 16:52 [PATCH iptables-nft 0/7] iptables: prefer native expressions for udp and tcp matches Florian Westphal
                   ` (6 preceding siblings ...)
  2022-01-25 16:53 ` [PATCH iptables-nft 7/7] nft: add support for native tcp flag matching Florian Westphal
@ 2022-01-29 12:44 ` Florian Westphal
  7 siblings, 0 replies; 9+ messages in thread
From: Florian Westphal @ 2022-01-29 12:44 UTC (permalink / raw)
  To: Florian Westphal; +Cc: netfilter-devel

Florian Westphal <fw@strlen.de> wrote:
> This series switches iptables-nft to use native nft expressions
> (payload, cmp, range, bitwise) to match on ports and tcp flags.
> 
> Patches are split up to first add delinearization support and
> then switch the add/insert side over to generating those expressions.

I pushed this series out, with "_complete_" replaced with "_parse_"
in function names to make it more aligned with the other function
names.

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

end of thread, other threads:[~2022-01-29 12:44 UTC | newest]

Thread overview: 9+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2022-01-25 16:52 [PATCH iptables-nft 0/7] iptables: prefer native expressions for udp and tcp matches Florian Westphal
2022-01-25 16:52 ` [PATCH iptables-nft 1/7] nft-shared: support native tcp port delinearize Florian Westphal
2022-01-25 16:52 ` [PATCH iptables-nft 2/7] nft-shared: support native tcp port range delinearize Florian Westphal
2022-01-25 16:52 ` [PATCH iptables-nft 3/7] nft-shared: support native udp port delinearize Florian Westphal
2022-01-25 16:52 ` [PATCH iptables-nft 4/7] nft: prefer native expressions instead of udp match Florian Westphal
2022-01-25 16:52 ` [PATCH iptables-nft 5/7] nft: prefer native expressions instead of tcp match Florian Westphal
2022-01-25 16:53 ` [PATCH iptables-nft 6/7] nft-shared: add tcp flag dissection Florian Westphal
2022-01-25 16:53 ` [PATCH iptables-nft 7/7] nft: add support for native tcp flag matching Florian Westphal
2022-01-29 12:44 ` [PATCH iptables-nft 0/7] iptables: prefer native expressions for udp and tcp matches Florian Westphal

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