netfilter-devel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH nft] nft-shared: support native tcp port delinearize
@ 2022-01-15 15:03 Florian Westphal
  2022-01-15 16:28 ` Pablo Neira Ayuso
  0 siblings, 1 reply; 3+ messages in thread
From: Florian Westphal @ 2022-01-15 15:03 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.

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

diff --git a/iptables/nft-shared.c b/iptables/nft-shared.c
index 4394e8b7c4e8..f027c1b282ab 100644
--- a/iptables/nft-shared.c
+++ b/iptables/nft-shared.c
@@ -466,6 +466,154 @@ 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 void nft_complete_tcp(struct nft_xt_ctx *ctx,
+			     struct iptables_command_state *cs,
+			     int sport, int dport,
+			     uint8_t op)
+{
+	struct xtables_match *match;
+	struct xt_tcp *tcp;
+
+	match = nft_create_match(ctx, cs, "tcp");
+	if (!match)
+		return;
+
+	tcp = (void*)match->m->data;
+	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:
+		switch (len) {
+		case 2:
+			port = ntohs(nftnl_expr_get_u16(e, NFTNL_EXPR_CMP_DATA));
+			nft_complete_th_port(ctx, cs, proto, port, -1, op);
+			return;
+		case 4:
+			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:
+		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;
+		}
+	}
+}
+
 static void nft_parse_cmp(struct nft_xt_ctx *ctx, struct nftnl_expr *e)
 {
 	void *data = ctx->cs;
@@ -481,7 +629,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);
+		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;
+		}
 		ctx->flags &= ~NFT_XT_CTX_PAYLOAD;
 	}
 }
-- 
2.34.1


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

* Re: [PATCH nft] nft-shared: support native tcp port delinearize
  2022-01-15 15:03 [PATCH nft] nft-shared: support native tcp port delinearize Florian Westphal
@ 2022-01-15 16:28 ` Pablo Neira Ayuso
  2022-01-16 19:00   ` Florian Westphal
  0 siblings, 1 reply; 3+ messages in thread
From: Pablo Neira Ayuso @ 2022-01-15 16:28 UTC (permalink / raw)
  To: Florian Westphal; +Cc: netfilter-devel

On Sat, Jan 15, 2022 at 04:03:16PM +0100, Florian Westphal wrote:
> 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

You can probably use the range expression, it has been there already
for quite some time and it is slightly more efficient than two cmp
expressions. nft still uses cmp for ranges for backward compatibility
reasons (range support is available since 4.9 and -stable 4.4 enters
EOL next month apparently), it only uses range for tcp dport != 0-1023.

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

Using the native payload for transport in the near future sounds a
good idea to me.

> Signed-off-by: Florian Westphal <fw@strlen.de>
> ---
>  iptables/nft-shared.c | 161 +++++++++++++++++++++++++++++++++++++++++-
>  1 file changed, 160 insertions(+), 1 deletion(-)
> 
> diff --git a/iptables/nft-shared.c b/iptables/nft-shared.c
> index 4394e8b7c4e8..f027c1b282ab 100644
> --- a/iptables/nft-shared.c
> +++ b/iptables/nft-shared.c
> @@ -466,6 +466,154 @@ 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 void nft_complete_tcp(struct nft_xt_ctx *ctx,
> +			     struct iptables_command_state *cs,
> +			     int sport, int dport,
> +			     uint8_t op)
> +{
> +	struct xtables_match *match;
> +	struct xt_tcp *tcp;
> +
> +	match = nft_create_match(ctx, cs, "tcp");
> +	if (!match)
> +		return;
> +
> +	tcp = (void*)match->m->data;
> +	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:
> +		switch (len) {
> +		case 2:
> +			port = ntohs(nftnl_expr_get_u16(e, NFTNL_EXPR_CMP_DATA));
> +			nft_complete_th_port(ctx, cs, proto, port, -1, op);
> +			return;
> +		case 4:
> +			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:
> +		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;
> +		}
> +	}
> +}
> +
>  static void nft_parse_cmp(struct nft_xt_ctx *ctx, struct nftnl_expr *e)
>  {
>  	void *data = ctx->cs;
> @@ -481,7 +629,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);
> +		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;
> +		}
>  		ctx->flags &= ~NFT_XT_CTX_PAYLOAD;
>  	}
>  }
> -- 
> 2.34.1
> 

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

* Re: [PATCH nft] nft-shared: support native tcp port delinearize
  2022-01-15 16:28 ` Pablo Neira Ayuso
@ 2022-01-16 19:00   ` Florian Westphal
  0 siblings, 0 replies; 3+ messages in thread
From: Florian Westphal @ 2022-01-16 19:00 UTC (permalink / raw)
  To: Pablo Neira Ayuso; +Cc: Florian Westphal, netfilter-devel

Pablo Neira Ayuso <pablo@netfilter.org> wrote:
> > $ 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
> 
> You can probably use the range expression, it has been there already
> for quite some time and it is slightly more efficient than two cmp
> expressions. nft still uses cmp for ranges for backward compatibility
> reasons (range support is available since 4.9 and -stable 4.4 enters
> EOL next month apparently), it only uses range for tcp dport != 0-1023.

Thanks for the hint, this was broken indeed, I reworked this to handle
exsiting range handling via two cmp expressions.

range sounds good, will add support for it too.

> > This would allow to extend iptables-nft to prefer
> > native payload expressions for --sport,dport in the future.
> 
> Using the native payload for transport in the near future sounds a
> good idea to me.

Great, I will work on this once the reverse translation is working.

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

end of thread, other threads:[~2022-01-16 19:00 UTC | newest]

Thread overview: 3+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2022-01-15 15:03 [PATCH nft] nft-shared: support native tcp port delinearize Florian Westphal
2022-01-15 16:28 ` Pablo Neira Ayuso
2022-01-16 19:00   ` 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).