summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorPablo Neira Ayuso <pablo@netfilter.org>2023-01-02 15:36:33 +0100
committerPablo Neira Ayuso <pablo@netfilter.org>2023-01-02 15:36:33 +0100
commitc1f92755437ea4eee10950a196a5c749329ae5f6 (patch)
tree33c6f8fb7f262b274d494a91f3aa43c41dff5c9c /src
parent75b096c094a5ee1d6ef443f16d6b55fa3dd1f197 (diff)
src: add gre support
GRE has a number of fields that are conditional based on flags, which requires custom dependency code similar to icmp and icmpv6. Matching on optional fields is not supported at this stage. Since this is a layer 3 tunnel protocol, an implicit dependency on NFT_META_L4PROTO for IPPROTO_GRE is generated. To achieve this, this patch adds new infrastructure to remove an outer dependency based on the inner protocol from delinearize path. Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
Diffstat (limited to 'src')
-rw-r--r--src/evaluate.c43
-rw-r--r--src/netlink_delinearize.c48
-rw-r--r--src/parser_bison.y33
-rw-r--r--src/payload.c47
-rw-r--r--src/proto.c27
-rw-r--r--src/scanner.l7
6 files changed, 188 insertions, 17 deletions
diff --git a/src/evaluate.c b/src/evaluate.c
index e98ea424..21de1840 100644
--- a/src/evaluate.c
+++ b/src/evaluate.c
@@ -881,24 +881,43 @@ static int expr_evaluate_payload(struct eval_ctx *ctx, struct expr **exprp)
static int expr_evaluate_inner(struct eval_ctx *ctx, struct expr **exprp)
{
struct proto_ctx *pctx = eval_proto_ctx(ctx);
- const struct proto_desc *desc;
+ const struct proto_desc *desc = NULL;
struct expr *expr = *exprp;
int ret;
- desc = pctx->protocol[expr->payload.inner_desc->base - 1].desc;
- if (!desc) {
- return expr_error(ctx->msgs, expr,
- "no transport protocol specified");
- }
+ assert(expr->etype == EXPR_PAYLOAD);
- if (proto_find_num(desc, expr->payload.inner_desc) < 0) {
- return expr_error(ctx->msgs, expr,
- "unexpected transport protocol %s",
- desc->name);
+ pctx = eval_proto_ctx(ctx);
+ desc = pctx->protocol[PROTO_BASE_TRANSPORT_HDR].desc;
+
+ if (desc == NULL &&
+ expr->payload.inner_desc->base < PROTO_BASE_INNER_HDR) {
+ struct stmt *nstmt;
+
+ if (payload_gen_inner_dependency(ctx, expr, &nstmt) < 0)
+ return -1;
+
+ rule_stmt_insert_at(ctx->rule, nstmt, ctx->stmt);
+
+ proto_ctx_update(pctx, PROTO_BASE_TRANSPORT_HDR, &expr->location, expr->payload.inner_desc);
}
- proto_ctx_update(pctx, PROTO_BASE_INNER_HDR, &expr->location,
- expr->payload.inner_desc);
+ if (expr->payload.inner_desc->base == PROTO_BASE_INNER_HDR) {
+ desc = pctx->protocol[expr->payload.inner_desc->base - 1].desc;
+ if (!desc) {
+ return expr_error(ctx->msgs, expr,
+ "no transport protocol specified");
+ }
+
+ if (proto_find_num(desc, expr->payload.inner_desc) < 0) {
+ return expr_error(ctx->msgs, expr,
+ "unexpected transport protocol %s",
+ desc->name);
+ }
+
+ proto_ctx_update(pctx, expr->payload.inner_desc->base, &expr->location,
+ expr->payload.inner_desc);
+ }
if (expr->payload.base != PROTO_BASE_INNER_HDR)
ctx->inner_desc = expr->payload.inner_desc;
diff --git a/src/netlink_delinearize.c b/src/netlink_delinearize.c
index 6be181e1..4f99dabb 100644
--- a/src/netlink_delinearize.c
+++ b/src/netlink_delinearize.c
@@ -35,6 +35,11 @@ struct dl_proto_ctx *dl_proto_ctx(struct rule_pp_ctx *ctx)
return ctx->dl;
}
+static struct dl_proto_ctx *dl_proto_ctx_outer(struct rule_pp_ctx *ctx)
+{
+ return &ctx->_dl[0];
+}
+
static int netlink_parse_expr(const struct nftnl_expr *nle,
struct netlink_parse_ctx *ctx);
@@ -1960,6 +1965,36 @@ struct stmt *netlink_parse_set_expr(const struct set *set,
return pctx->stmt;
}
+static bool meta_outer_may_dependency_kill(struct rule_pp_ctx *ctx,
+ const struct expr *expr)
+{
+ struct dl_proto_ctx *dl_outer = dl_proto_ctx_outer(ctx);
+ struct stmt *stmt = dl_outer->pdctx.pdeps[expr->payload.inner_desc->base];
+ struct expr *dep;
+ uint8_t l4proto;
+
+ if (!stmt)
+ return false;
+
+ dep = stmt->expr;
+
+ if (dep->left->meta.key != NFT_META_L4PROTO)
+ return false;
+
+ l4proto = mpz_get_uint8(dep->right->value);
+
+ switch (l4proto) {
+ case IPPROTO_GRE:
+ if (expr->payload.inner_desc == &proto_gre)
+ return true;
+ break;
+ default:
+ break;
+ }
+
+ return false;
+}
+
static void expr_postprocess(struct rule_pp_ctx *ctx, struct expr **exprp);
static void payload_match_expand(struct rule_pp_ctx *ctx,
@@ -2003,6 +2038,12 @@ static void payload_match_expand(struct rule_pp_ctx *ctx,
nexpr->left->payload.tmpl = expr->left->payload.tmpl;
}
nexpr->left->payload.inner_desc = expr->left->payload.inner_desc;
+
+ if (meta_outer_may_dependency_kill(ctx, expr->left)) {
+ struct dl_proto_ctx *dl_outer = dl_proto_ctx_outer(ctx);
+
+ payload_dependency_release(&dl_outer->pdctx, expr->left->payload.inner_desc->base);
+ }
}
if (payload_is_stacked(dl->pctx.protocol[base].desc, nexpr))
@@ -2747,6 +2788,13 @@ static void expr_postprocess(struct rule_pp_ctx *ctx, struct expr **exprp)
break;
case EXPR_PAYLOAD:
payload_expr_complete(expr, &dl->pctx);
+ if (expr->payload.inner_desc) {
+ if (meta_outer_may_dependency_kill(ctx, expr)) {
+ struct dl_proto_ctx *dl_outer = dl_proto_ctx_outer(ctx);
+
+ payload_dependency_release(&dl_outer->pdctx, expr->payload.inner_desc->base);
+ }
+ }
payload_dependency_kill(&dl->pdctx, expr, dl->pctx.family);
break;
case EXPR_VALUE:
diff --git a/src/parser_bison.y b/src/parser_bison.y
index c48283ee..b2973995 100644
--- a/src/parser_bison.y
+++ b/src/parser_bison.y
@@ -442,6 +442,8 @@ int nft_lex(void *, void *, void *);
%token VXLAN "vxlan"
%token VNI "vni"
+%token GRE "gre"
+
%token SCTP "sctp"
%token CHUNK "chunk"
%token DATA "data"
@@ -902,9 +904,9 @@ int nft_lex(void *, void *, void *);
%type <val> tcpopt_field_maxseg tcpopt_field_mptcp tcpopt_field_sack tcpopt_field_tsopt tcpopt_field_window
%type <tcp_kind_field> tcp_hdr_option_kind_and_field
-%type <expr> inner_eth_expr inner_inet_expr inner_expr vxlan_hdr_expr
-%destructor { expr_free($$); } inner_eth_expr inner_inet_expr inner_expr vxlan_hdr_expr
-%type <val> vxlan_hdr_field
+%type <expr> inner_eth_expr inner_inet_expr inner_expr vxlan_hdr_expr gre_hdr_expr
+%destructor { expr_free($$); } inner_eth_expr inner_inet_expr inner_expr vxlan_hdr_expr gre_hdr_expr
+%type <val> vxlan_hdr_field gre_hdr_field
%type <stmt> optstrip_stmt
%destructor { stmt_free($$); } optstrip_stmt
@@ -967,6 +969,7 @@ close_scope_export : { scanner_pop_start_cond(nft->scanner, PARSER_SC_CMD_EXPORT
close_scope_fib : { scanner_pop_start_cond(nft->scanner, PARSER_SC_EXPR_FIB); };
close_scope_frag : { scanner_pop_start_cond(nft->scanner, PARSER_SC_EXPR_FRAG); };
close_scope_fwd : { scanner_pop_start_cond(nft->scanner, PARSER_SC_STMT_FWD); };
+close_scope_gre : { scanner_pop_start_cond(nft->scanner, PARSER_SC_GRE); };
close_scope_hash : { scanner_pop_start_cond(nft->scanner, PARSER_SC_EXPR_HASH); };
close_scope_hbh : { scanner_pop_start_cond(nft->scanner, PARSER_SC_EXPR_HBH); };
close_scope_ip : { scanner_pop_start_cond(nft->scanner, PARSER_SC_IP); };
@@ -4881,6 +4884,13 @@ primary_rhs_expr : symbol_expr { $$ = $1; }
BYTEORDER_HOST_ENDIAN,
sizeof(data) * BITS_PER_BYTE, &data);
}
+ | GRE close_scope_gre
+ {
+ uint8_t data = IPPROTO_GRE;
+ $$ = constant_expr_alloc(&@$, &inet_protocol_type,
+ BYTEORDER_HOST_ENDIAN,
+ sizeof(data) * BITS_PER_BYTE, &data);
+ }
| COMP close_scope_comp
{
uint8_t data = IPPROTO_COMP;
@@ -5337,6 +5347,7 @@ payload_expr : payload_raw_expr
| sctp_hdr_expr
| th_hdr_expr
| vxlan_hdr_expr
+ | gre_hdr_expr
;
payload_raw_expr : AT payload_base_spec COMMA NUM COMMA NUM close_scope_at
@@ -5633,6 +5644,22 @@ vxlan_hdr_field : VNI { $$ = VXLANHDR_VNI; }
| FLAGS { $$ = VXLANHDR_FLAGS; }
;
+gre_hdr_expr : GRE gre_hdr_field close_scope_gre
+ {
+ $$ = payload_expr_alloc(&@$, &proto_gre, $2);
+ }
+ | GRE close_scope_gre inner_inet_expr
+ {
+ $$ = $3;
+ $$->payload.inner_desc = &proto_gre;
+ }
+ ;
+
+gre_hdr_field : HDRVERSION { $$ = GREHDR_VERSION; }
+ | FLAGS { $$ = GREHDR_FLAGS; }
+ | PROTOCOL { $$ = GREHDR_PROTOCOL; }
+ ;
+
optstrip_stmt : RESET TCP OPTION tcp_hdr_option_type close_scope_tcp
{
$$ = optstrip_stmt_alloc(&@$, tcpopt_expr_alloc(&@$,
diff --git a/src/payload.c b/src/payload.c
index 71b29a09..ed76623c 100644
--- a/src/payload.c
+++ b/src/payload.c
@@ -487,6 +487,14 @@ payload_gen_special_dependency(struct eval_ctx *ctx, const struct expr *expr)
break;
}
+ /* this tunnel protocol does not encapsulate an inner
+ * link layer, use proto_netdev which relies on
+ * NFT_META_PROTOCOL for dependencies.
+ */
+ if (expr->payload.inner_desc &&
+ !(expr->payload.inner_desc->inner.flags & NFT_INNER_LL))
+ desc = &proto_netdev;
+
desc_upper = &proto_ip6;
if (expr->payload.desc == &proto_icmp ||
expr->payload.desc == &proto_igmp)
@@ -1357,3 +1365,42 @@ bad_proto:
return expr_error(ctx->msgs, expr, "incompatible icmp match: rule has %d, need %u",
pctx->th_dep.icmp.type, type);
}
+
+int payload_gen_inner_dependency(struct eval_ctx *ctx, const struct expr *expr,
+ struct stmt **res)
+{
+ struct proto_ctx *pctx = eval_proto_ctx(ctx);
+ const struct proto_hdr_template *tmpl;
+ const struct proto_desc *desc, *inner_desc;
+ struct expr *left, *right, *dep;
+ struct stmt *stmt = NULL;
+ int protocol;
+
+ assert(expr->etype == EXPR_PAYLOAD);
+
+ inner_desc = expr->payload.inner_desc;
+ desc = pctx->protocol[inner_desc->base - 1].desc;
+ if (desc == NULL)
+ desc = &proto_ip;
+
+ tmpl = &inner_desc->templates[0];
+ assert(tmpl);
+
+ protocol = proto_find_num(desc, inner_desc);
+ if (protocol < 0)
+ return expr_error(ctx->msgs, expr,
+ "conflicting protocols specified: %s vs. %s",
+ desc->name, inner_desc->name);
+
+ left = meta_expr_alloc(&expr->location, tmpl->meta_key);
+
+ right = constant_expr_alloc(&expr->location, tmpl->dtype,
+ tmpl->dtype->byteorder, tmpl->len,
+ constant_data_ptr(protocol, tmpl->len));
+
+ dep = relational_expr_alloc(&expr->location, OP_EQ, left, right);
+ stmt = expr_stmt_alloc(&dep->location, dep);
+
+ *res = stmt;
+ return 0;
+}
diff --git a/src/proto.c b/src/proto.c
index e6c1100b..3bb4ae74 100644
--- a/src/proto.c
+++ b/src/proto.c
@@ -90,6 +90,7 @@ int proto_find_num(const struct proto_desc *base,
static const struct proto_desc *inner_protocols[] = {
&proto_vxlan,
+ &proto_gre,
};
const struct proto_desc *proto_find_inner(uint32_t type, uint32_t hdrsize,
@@ -771,6 +772,28 @@ const struct datatype ecn_type = {
.sym_tbl = &ecn_type_tbl,
};
+#define GREHDR_TEMPLATE(__name, __dtype, __member) \
+ HDR_TEMPLATE(__name, __dtype, struct grehdr, __member)
+#define GREHDR_TYPE(__name, __member) \
+ GREHDR_TEMPLATE(__name, &ethertype_type, __member)
+
+const struct proto_desc proto_gre = {
+ .name = "gre",
+ .id = PROTO_DESC_GRE,
+ .base = PROTO_BASE_TRANSPORT_HDR,
+ .templates = {
+ [0] = PROTO_META_TEMPLATE("l4proto", &inet_protocol_type, NFT_META_L4PROTO, 8),
+ [GREHDR_FLAGS] = HDR_BITFIELD("flags", &integer_type, 0, 5),
+ [GREHDR_VERSION] = HDR_BITFIELD("version", &integer_type, 13, 3),
+ [GREHDR_PROTOCOL] = HDR_BITFIELD("protocol", &ethertype_type, 16, 16),
+ },
+ .inner = {
+ .hdrsize = sizeof(struct grehdr),
+ .flags = NFT_INNER_NH | NFT_INNER_TH,
+ .type = NFT_INNER_GENEVE + 1,
+ },
+};
+
#define IPHDR_FIELD(__name, __member) \
HDR_FIELD(__name, struct iphdr, __member)
#define IPHDR_ADDR(__name, __member) \
@@ -794,6 +817,7 @@ const struct proto_desc proto_ip = {
PROTO_LINK(IPPROTO_TCP, &proto_tcp),
PROTO_LINK(IPPROTO_DCCP, &proto_dccp),
PROTO_LINK(IPPROTO_SCTP, &proto_sctp),
+ PROTO_LINK(IPPROTO_GRE, &proto_gre),
},
.templates = {
[0] = PROTO_META_TEMPLATE("l4proto", &inet_protocol_type, NFT_META_L4PROTO, 8),
@@ -920,6 +944,7 @@ const struct proto_desc proto_ip6 = {
PROTO_LINK(IPPROTO_ICMP, &proto_icmp),
PROTO_LINK(IPPROTO_IGMP, &proto_igmp),
PROTO_LINK(IPPROTO_ICMPV6, &proto_icmp6),
+ PROTO_LINK(IPPROTO_GRE, &proto_gre),
},
.templates = {
[0] = PROTO_META_TEMPLATE("l4proto", &inet_protocol_type, NFT_META_L4PROTO, 8),
@@ -985,6 +1010,7 @@ const struct proto_desc proto_inet_service = {
PROTO_LINK(IPPROTO_ICMP, &proto_icmp),
PROTO_LINK(IPPROTO_IGMP, &proto_igmp),
PROTO_LINK(IPPROTO_ICMPV6, &proto_icmp6),
+ PROTO_LINK(IPPROTO_GRE, &proto_gre),
},
.templates = {
[0] = PROTO_META_TEMPLATE("l4proto", &inet_protocol_type, NFT_META_L4PROTO, 8),
@@ -1226,6 +1252,7 @@ static const struct proto_desc *proto_definitions[PROTO_DESC_MAX + 1] = {
[PROTO_DESC_VLAN] = &proto_vlan,
[PROTO_DESC_ETHER] = &proto_eth,
[PROTO_DESC_VXLAN] = &proto_vxlan,
+ [PROTO_DESC_GRE] = &proto_gre,
};
const struct proto_desc *proto_find_desc(enum proto_desc_id desc_id)
diff --git a/src/scanner.l b/src/scanner.l
index 61a49286..3d9888ab 100644
--- a/src/scanner.l
+++ b/src/scanner.l
@@ -201,6 +201,7 @@ addrstring ({macaddr}|{ip4addr}|{ip6addr})
%s SCANSTATE_CT
%s SCANSTATE_COUNTER
%s SCANSTATE_ETH
+%s SCANSTATE_GRE
%s SCANSTATE_ICMP
%s SCANSTATE_IGMP
%s SCANSTATE_IP
@@ -492,7 +493,7 @@ addrstring ({macaddr}|{ip4addr}|{ip6addr})
}
"ip" { scanner_push_start_cond(yyscanner, SCANSTATE_IP); return IP; }
-<SCANSTATE_IP,SCANSTATE_IP6,SCANSTATE_EXPR_OSF>{
+<SCANSTATE_IP,SCANSTATE_IP6,SCANSTATE_EXPR_OSF,SCANSTATE_GRE>{
"version" { return HDRVERSION; }
}
<SCANSTATE_EXPR_AH,SCANSTATE_EXPR_DST,SCANSTATE_EXPR_HBH,SCANSTATE_EXPR_MH,SCANSTATE_EXPR_RT,SCANSTATE_IP>{
@@ -509,7 +510,7 @@ addrstring ({macaddr}|{ip4addr}|{ip6addr})
<SCANSTATE_EXPR_OSF,SCANSTATE_IP>{
"ttl" { return TTL; }
}
-<SCANSTATE_CT,SCANSTATE_IP,SCANSTATE_META,SCANSTATE_TYPE>"protocol" { return PROTOCOL; }
+<SCANSTATE_CT,SCANSTATE_IP,SCANSTATE_META,SCANSTATE_TYPE,SCANSTATE_GRE>"protocol" { return PROTOCOL; }
<SCANSTATE_EXPR_MH,SCANSTATE_EXPR_UDP,SCANSTATE_EXPR_UDPLITE,SCANSTATE_ICMP,SCANSTATE_IGMP,SCANSTATE_IP,SCANSTATE_SCTP,SCANSTATE_TCP>{
"checksum" { return CHECKSUM; }
}
@@ -624,6 +625,8 @@ addrstring ({macaddr}|{ip4addr}|{ip6addr})
"vxlan" { return VXLAN; }
"vni" { return VNI; }
+"gre" { scanner_push_start_cond(yyscanner, SCANSTATE_GRE); return GRE; }
+
"tcp" { scanner_push_start_cond(yyscanner, SCANSTATE_TCP); return TCP; }
"dccp" { scanner_push_start_cond(yyscanner, SCANSTATE_EXPR_DCCP); return DCCP; }