summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorStephen Suryaputra <ssuryaextr@gmail.com>2019-07-03 20:30:52 -0400
committerPablo Neira Ayuso <pablo@netfilter.org>2019-07-04 14:29:08 +0200
commit226a0e072d5c1edeb53cb61b959b011168c5c29a (patch)
tree07e43268efe15dc8b64b8ca9baca71e02239213f /src
parent1694c01c30fba06461ca82ede070bf6a9cd9a4db (diff)
exthdr: add support for matching IPv4 options
Add capability to have rules matching IPv4 options. This is developed mainly to support dropping of IP packets with loose and/or strict source route route options. Signed-off-by: Stephen Suryaputra <ssuryaextr@gmail.com> Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
Diffstat (limited to 'src')
-rw-r--r--src/Makefile.am1
-rw-r--r--src/evaluate.c17
-rw-r--r--src/exthdr.c22
-rw-r--r--src/json.c8
-rw-r--r--src/parser_bison.y31
-rw-r--r--src/parser_json.c61
-rw-r--r--src/payload.c4
-rw-r--r--src/scanner.l8
8 files changed, 150 insertions, 2 deletions
diff --git a/src/Makefile.am b/src/Makefile.am
index 9ad7e1f2..e2b53139 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -40,6 +40,7 @@ libnftables_la_SOURCES = \
exthdr.c \
fib.c \
hash.c \
+ ipopt.c \
meta.c \
rt.c \
numgen.c \
diff --git a/src/evaluate.c b/src/evaluate.c
index 19c2d4c6..8086f750 100644
--- a/src/evaluate.c
+++ b/src/evaluate.c
@@ -513,6 +513,20 @@ static int __expr_evaluate_exthdr(struct eval_ctx *ctx, struct expr **exprp)
totlen, max_tcpoptlen);
break;
}
+ case NFT_EXTHDR_OP_IPV4: {
+ static const unsigned int max_ipoptlen = 40 * BITS_PER_BYTE;
+ unsigned int totlen = 0;
+
+ totlen += expr->exthdr.tmpl->offset;
+ totlen += expr->exthdr.tmpl->len;
+ totlen += expr->exthdr.offset;
+
+ if (totlen > max_ipoptlen)
+ return expr_error(ctx->msgs, expr,
+ "offset and size %u exceeds max ip option len (%u)",
+ totlen, max_ipoptlen);
+ break;
+ }
default:
break;
}
@@ -537,6 +551,9 @@ static int expr_evaluate_exthdr(struct eval_ctx *ctx, struct expr **exprp)
dependency = &proto_tcp;
pb = PROTO_BASE_TRANSPORT_HDR;
break;
+ case NFT_EXTHDR_OP_IPV4:
+ dependency = &proto_ip;
+ break;
case NFT_EXTHDR_OP_IPV6:
default:
dependency = &proto_ip6;
diff --git a/src/exthdr.c b/src/exthdr.c
index c9c2bf50..e1ec6f3d 100644
--- a/src/exthdr.c
+++ b/src/exthdr.c
@@ -38,6 +38,11 @@ static void exthdr_expr_print(const struct expr *expr, struct output_ctx *octx)
if (offset)
nft_print(octx, "%d", offset);
nft_print(octx, " %s", expr->exthdr.tmpl->token);
+ } else if (expr->exthdr.op == NFT_EXTHDR_OP_IPV4) {
+ nft_print(octx, "ip option %s", expr->exthdr.desc->name);
+ if (expr->exthdr.flags & NFT_EXTHDR_F_PRESENT)
+ return;
+ nft_print(octx, " %s", expr->exthdr.tmpl->token);
} else {
if (expr->exthdr.flags & NFT_EXTHDR_F_PRESENT)
nft_print(octx, "exthdr %s", expr->exthdr.desc->name);
@@ -172,6 +177,8 @@ void exthdr_init_raw(struct expr *expr, uint8_t type,
assert(expr->etype == EXPR_EXTHDR);
if (op == NFT_EXTHDR_OP_TCPOPT)
return tcpopt_init_raw(expr, type, offset, len, flags);
+ if (op == NFT_EXTHDR_OP_IPV4)
+ return ipopt_init_raw(expr, type, offset, len, flags, true);
expr->len = len;
expr->exthdr.flags = flags;
@@ -222,7 +229,8 @@ bool exthdr_find_template(struct expr *expr, const struct expr *mask, unsigned i
{
unsigned int off, mask_offset, mask_len;
- if (expr->exthdr.tmpl != &exthdr_unknown_template)
+ if (expr->exthdr.op != NFT_EXTHDR_OP_IPV4 &&
+ expr->exthdr.tmpl != &exthdr_unknown_template)
return false;
/* In case we are handling tcp options instead of the default ipv6
@@ -237,8 +245,18 @@ bool exthdr_find_template(struct expr *expr, const struct expr *mask, unsigned i
off = expr->exthdr.offset;
off += round_up(mask->len, BITS_PER_BYTE) - mask_len;
+ /* Handle ip options after the offset and mask have been calculated. */
+ if (expr->exthdr.op == NFT_EXTHDR_OP_IPV4) {
+ if (ipopt_find_template(expr, off, mask_len - mask_offset)) {
+ *shift = mask_offset;
+ return true;
+ } else {
+ return false;
+ }
+ }
+
exthdr_init_raw(expr, expr->exthdr.desc->type,
- off, mask_len - mask_offset, NFT_EXTHDR_OP_IPV6, 0);
+ off, mask_len - mask_offset, expr->exthdr.op, 0);
/* still failed to find a template... Bug. */
if (expr->exthdr.tmpl == &exthdr_unknown_template)
diff --git a/src/json.c b/src/json.c
index 1484c21b..1006d7bb 100644
--- a/src/json.c
+++ b/src/json.c
@@ -634,6 +634,14 @@ json_t *exthdr_expr_json(const struct expr *expr, struct output_ctx *octx)
return json_pack("{s:o}", "tcp option", root);
}
+ if (expr->exthdr.op == NFT_EXTHDR_OP_IPV4) {
+ root = json_pack("{s:s}", "name", desc);
+
+ if (!is_exists)
+ json_object_set_new(root, "field", json_string(field));
+
+ return json_pack("{s:o}", "ip option", root);
+ }
root = json_pack("{s:s}",
"name", desc);
diff --git a/src/parser_bison.y b/src/parser_bison.y
index 153ef326..a4905f2a 100644
--- a/src/parser_bison.y
+++ b/src/parser_bison.y
@@ -309,6 +309,14 @@ int nft_lex(void *, void *, void *);
%token PROTOCOL "protocol"
%token CHECKSUM "checksum"
+%token PTR "ptr"
+%token VALUE "value"
+
+%token LSRR "lsrr"
+%token RR "rr"
+%token SSRR "ssrr"
+%token RA "ra"
+
%token ICMP "icmp"
%token CODE "code"
%token SEQUENCE "seq"
@@ -698,6 +706,7 @@ int nft_lex(void *, void *, void *);
%type <expr> ip_hdr_expr icmp_hdr_expr igmp_hdr_expr numgen_expr hash_expr
%destructor { expr_free($$); } ip_hdr_expr icmp_hdr_expr igmp_hdr_expr numgen_expr hash_expr
%type <val> ip_hdr_field icmp_hdr_field igmp_hdr_field
+%type <val> ip_option_type ip_option_field
%type <expr> ip6_hdr_expr icmp6_hdr_expr
%destructor { expr_free($$); } ip6_hdr_expr icmp6_hdr_expr
%type <val> ip6_hdr_field icmp6_hdr_field
@@ -4249,6 +4258,15 @@ ip_hdr_expr : IP ip_hdr_field
{
$$ = payload_expr_alloc(&@$, &proto_ip, $2);
}
+ | IP OPTION ip_option_type ip_option_field
+ {
+ $$ = ipopt_expr_alloc(&@$, $3, $4, 0);
+ }
+ | IP OPTION ip_option_type
+ {
+ $$ = ipopt_expr_alloc(&@$, $3, IPOPT_FIELD_TYPE, 0);
+ $$->exthdr.flags = NFT_EXTHDR_F_PRESENT;
+ }
;
ip_hdr_field : HDRVERSION { $$ = IPHDR_VERSION; }
@@ -4265,6 +4283,19 @@ ip_hdr_field : HDRVERSION { $$ = IPHDR_VERSION; }
| DADDR { $$ = IPHDR_DADDR; }
;
+ip_option_type : LSRR { $$ = IPOPT_LSRR; }
+ | RR { $$ = IPOPT_RR; }
+ | SSRR { $$ = IPOPT_SSRR; }
+ | RA { $$ = IPOPT_RA; }
+ ;
+
+ip_option_field : TYPE { $$ = IPOPT_FIELD_TYPE; }
+ | LENGTH { $$ = IPOPT_FIELD_LENGTH; }
+ | VALUE { $$ = IPOPT_FIELD_VALUE; }
+ | PTR { $$ = IPOPT_FIELD_PTR; }
+ | ADDR { $$ = IPOPT_FIELD_ADDR_0; }
+ ;
+
icmp_hdr_expr : ICMP icmp_hdr_field
{
$$ = payload_expr_alloc(&@$, &proto_icmp, $2);
diff --git a/src/parser_json.c b/src/parser_json.c
index 30b17173..f701ebdf 100644
--- a/src/parser_json.c
+++ b/src/parser_json.c
@@ -587,6 +587,66 @@ static struct expr *json_parse_tcp_option_expr(struct json_ctx *ctx,
return tcpopt_expr_alloc(int_loc, descval, fieldval);
}
+static int json_parse_ip_option_type(const char *name, int *val)
+{
+ unsigned int i;
+
+ for (i = 0; i < array_size(ipopt_protocols); i++) {
+ if (ipopt_protocols[i] &&
+ !strcmp(ipopt_protocols[i]->name, name)) {
+ if (val)
+ *val = i;
+ return 0;
+ }
+ }
+ return 1;
+}
+
+static int json_parse_ip_option_field(int type, const char *name, int *val)
+{
+ unsigned int i;
+ const struct exthdr_desc *desc = ipopt_protocols[type];
+
+ for (i = 0; i < array_size(desc->templates); i++) {
+ if (desc->templates[i].token &&
+ !strcmp(desc->templates[i].token, name)) {
+ if (val)
+ *val = i;
+ return 0;
+ }
+ }
+ return 1;
+}
+
+static struct expr *json_parse_ip_option_expr(struct json_ctx *ctx,
+ const char *type, json_t *root)
+{
+ const char *desc, *field;
+ int descval, fieldval;
+ struct expr *expr;
+
+ if (json_unpack_err(ctx, root, "{s:s}", "name", &desc))
+ return NULL;
+
+ if (json_parse_ip_option_type(desc, &descval)) {
+ json_error(ctx, "Unknown ip option name '%s'.", desc);
+ return NULL;
+ }
+
+ if (json_unpack(root, "{s:s}", "field", &field)) {
+ expr = ipopt_expr_alloc(int_loc, descval,
+ IPOPT_FIELD_TYPE, 0);
+ expr->exthdr.flags = NFT_EXTHDR_F_PRESENT;
+
+ return expr;
+ }
+ if (json_parse_ip_option_field(descval, field, &fieldval)) {
+ json_error(ctx, "Unknown ip option field '%s'.", field);
+ return NULL;
+ }
+ return ipopt_expr_alloc(int_loc, descval, fieldval, 0);
+}
+
static const struct exthdr_desc *exthdr_lookup_byname(const char *name)
{
const struct exthdr_desc *exthdr_tbl[] = {
@@ -1291,6 +1351,7 @@ static struct expr *json_parse_expr(struct json_ctx *ctx, json_t *root)
{ "payload", json_parse_payload_expr, CTX_F_STMT | CTX_F_PRIMARY | CTX_F_SET_RHS | CTX_F_MANGLE | CTX_F_SES | CTX_F_MAP },
{ "exthdr", json_parse_exthdr_expr, CTX_F_PRIMARY | CTX_F_SET_RHS | CTX_F_SES | CTX_F_MAP },
{ "tcp option", json_parse_tcp_option_expr, CTX_F_PRIMARY | CTX_F_SET_RHS | CTX_F_MANGLE | CTX_F_SES },
+ { "ip option", json_parse_ip_option_expr, CTX_F_PRIMARY | CTX_F_SET_RHS | CTX_F_MANGLE | CTX_F_SES },
{ "meta", json_parse_meta_expr, CTX_F_STMT | CTX_F_PRIMARY | CTX_F_SET_RHS | CTX_F_MANGLE | CTX_F_SES | CTX_F_MAP },
{ "osf", json_parse_osf_expr, CTX_F_STMT | CTX_F_PRIMARY | CTX_F_MAP },
{ "ipsec", json_parse_xfrm_expr, CTX_F_PRIMARY | CTX_F_MAP },
diff --git a/src/payload.c b/src/payload.c
index 7e4f935b..3bf1ecc7 100644
--- a/src/payload.c
+++ b/src/payload.c
@@ -542,6 +542,10 @@ void exthdr_dependency_kill(struct payload_dep_ctx *ctx, struct expr *expr,
if (payload_dependency_exists(ctx, PROTO_BASE_NETWORK_HDR))
payload_dependency_release(ctx);
break;
+ case NFT_EXTHDR_OP_IPV4:
+ if (payload_dependency_exists(ctx, PROTO_BASE_NETWORK_HDR))
+ payload_dependency_release(ctx);
+ break;
default:
break;
}
diff --git a/src/scanner.l b/src/scanner.l
index b46b25e7..7f6c0431 100644
--- a/src/scanner.l
+++ b/src/scanner.l
@@ -402,6 +402,14 @@ addrstring ({macaddr}|{ip4addr}|{ip6addr})
"protocol" { return PROTOCOL; }
"checksum" { return CHECKSUM; }
+"lsrr" { return LSRR; }
+"rr" { return RR; }
+"ssrr" { return SSRR; }
+"ra" { return RA; }
+
+"value" { return VALUE; }
+"ptr" { return PTR; }
+
"echo" { return ECHO; }
"eol" { return EOL; }
"maxseg" { return MAXSEG; }