summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
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; }