summaryrefslogtreecommitdiffstats
path: root/src/payload.c
diff options
context:
space:
mode:
authorFlorian Westphal <fw@strlen.de>2017-05-25 09:14:58 +0200
committerFlorian Westphal <fw@strlen.de>2017-05-25 09:16:38 +0200
commitbb6a7f201a817652dd2c795539236c9319a23ad7 (patch)
tree1d56b003ba39a44ef0acca8f777389b7eccad394 /src/payload.c
parent1e6ae0e42bdc161d178277c336886e18c259caf5 (diff)
parent5f46b18745d18c486e959c93da649c18c8b10fe0 (diff)
Merge branch 'meta_l4_dependency'
Currently nft inserts different types of dependencies for l4 protocols, depending on the family. For inet, nft inserts 'meta l4proto' to e.g. check for tcp, for ip, nft uses 'ip protocol'. Both are fine. The ip6 family however uses 'ip6 nexthdr', and thats a problem because e.g. tcp dport 22 will not match packets that use ipv6 extension headers. The series switches both ipv6 and ipv4 to use meta l4 instead so ipv6 will always check the last transport header value. We could ignore ip as only ipv6 uses extension headers. However, switching ipv4 as well makes things a bit simpler because nft then creates the same l4 dependency for all families. Acked-by: Pablo Neira Ayuso <pablo@netfilter.org> Signed-off-by: Florian Westphal <fw@strlen.de>
Diffstat (limited to 'src/payload.c')
-rw-r--r--src/payload.c80
1 files changed, 64 insertions, 16 deletions
diff --git a/src/payload.c b/src/payload.c
index 55128fee..11b6df37 100644
--- a/src/payload.c
+++ b/src/payload.c
@@ -117,6 +117,28 @@ static const struct expr_ops payload_expr_ops = {
.pctx_update = payload_expr_pctx_update,
};
+/*
+ * We normally use 'meta l4proto' to fetch the last l4 header of the
+ * ipv6 extension header chain so we will also match
+ * tcp after a fragmentation header, for instance.
+ * For consistency we also use meta l4proto for ipv4.
+ *
+ * If user specifically asks for nexthdr x, don't add another (useless)
+ * meta dependency.
+ */
+static bool proto_key_is_protocol(const struct proto_desc *desc, unsigned int type)
+{
+ if (type == desc->protocol_key)
+ return true;
+
+ if (desc == &proto_ip6 && type == IP6HDR_NEXTHDR)
+ return true;
+ if (desc == &proto_ip && type == IPHDR_PROTOCOL)
+ return true;
+
+ return false;
+}
+
struct expr *payload_expr_alloc(const struct location *loc,
const struct proto_desc *desc,
unsigned int type)
@@ -129,7 +151,7 @@ struct expr *payload_expr_alloc(const struct location *loc,
if (desc != NULL) {
tmpl = &desc->templates[type];
base = desc->base;
- if (type == desc->protocol_key)
+ if (proto_key_is_protocol(desc, type))
flags = EXPR_F_PROTOCOL;
} else {
tmpl = &proto_unknown_template;
@@ -224,26 +246,52 @@ static int payload_add_dependency(struct eval_ctx *ctx,
}
static const struct proto_desc *
+payload_get_get_ll_hdr(const struct eval_ctx *ctx)
+{
+ switch (ctx->pctx.family) {
+ case NFPROTO_INET:
+ return &proto_inet;
+ case NFPROTO_BRIDGE:
+ return &proto_eth;
+ case NFPROTO_NETDEV:
+ return &proto_netdev;
+ default:
+ break;
+ }
+
+ return NULL;
+}
+
+static const struct proto_desc *
payload_gen_special_dependency(struct eval_ctx *ctx, const struct expr *expr)
{
switch (expr->payload.base) {
case PROTO_BASE_LL_HDR:
- switch (ctx->pctx.family) {
- case NFPROTO_INET:
- return &proto_inet;
- case NFPROTO_BRIDGE:
- return &proto_eth;
- case NFPROTO_NETDEV:
- return &proto_netdev;
- default:
- break;
- }
- break;
+ return payload_get_get_ll_hdr(ctx);
case PROTO_BASE_TRANSPORT_HDR:
- if (expr->payload.desc == &proto_icmp)
- return &proto_ip;
- if (expr->payload.desc == &proto_icmp6)
- return &proto_ip6;
+ if (expr->payload.desc == &proto_icmp ||
+ expr->payload.desc == &proto_icmp6) {
+ const struct proto_desc *desc, *desc_upper;
+ struct stmt *nstmt;
+
+ desc = ctx->pctx.protocol[PROTO_BASE_LL_HDR].desc;
+ if (!desc) {
+ desc = payload_get_get_ll_hdr(ctx);
+ if (!desc)
+ break;
+ }
+
+ desc_upper = &proto_ip6;
+ if (expr->payload.desc == &proto_icmp)
+ desc_upper = &proto_ip;
+
+ if (payload_add_dependency(ctx, desc, desc_upper,
+ expr, &nstmt) < 0)
+ return NULL;
+
+ list_add_tail(&nstmt->list, &ctx->stmt->list);
+ return desc_upper;
+ }
return &proto_inet_service;
default:
break;