summaryrefslogtreecommitdiffstats
path: root/iptables/nft-shared.c
diff options
context:
space:
mode:
authorFlorian Westphal <fw@strlen.de>2022-01-25 17:52:55 +0100
committerFlorian Westphal <fw@strlen.de>2022-01-29 13:33:36 +0100
commit250dce876d924b9467ffa035af445912e86ea93b (patch)
tree751301bbe5e70794459498ca3f9fdaff1ab2154a /iptables/nft-shared.c
parentea5d45dc23a4529a2a45c59ac6c13b61297c789e (diff)
nft-shared: support native tcp port delinearize
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>
Diffstat (limited to 'iptables/nft-shared.c')
-rw-r--r--iptables/nft-shared.c178
1 files changed, 176 insertions, 2 deletions
diff --git a/iptables/nft-shared.c b/iptables/nft-shared.c
index d0d0d558..061893cf 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_parse_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_parse_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_parse_tcp(ctx, cs, sport, dport, op);
+ break;
+ }
+}
+
+static void nft_parse_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_parse_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_parse_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_parse_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_parse_transport(ctx, e, data);
+ break;
+ }
}
}