summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAlvaro Neira <alvaroneay@gmail.com>2014-09-30 17:21:40 +0200
committerPablo Neira Ayuso <pablo@netfilter.org>2014-10-09 13:53:11 +0200
commit5fdd0b6a0600e66f9ff6d9a1d6b749aa68a3ba99 (patch)
tree282a5201207e607a0ccc94c17cab9bebb12da723
parent67094206871b3dbaabd48894bc171e67010762c4 (diff)
nft: complete reject support
This patch allows to use the reject action in rules. For example: nft add rule filter input udp dport 22 reject In this rule, we assume that the reason is network unreachable. Also we can specify the reason with the option "with" and the reason. For example: nft add rule filter input tcp dport 22 reject with icmp type host-unreachable In the bridge tables and inet tables, we can use this action too. For example: nft add rule inet filter input reject with icmp type host-unreachable In this rule above, this generates a meta nfproto dependency to match ipv4 traffic because we use a icmpv4 reason to reject. If the reason is not specified, we infer it from the context. Moreover, we have the new icmpx datatype. You can use this datatype for the bridge and the inet tables to simplify your ruleset. For example: nft add rule inet filter input reject with icmpx type host-unreachable We have four icmpx reason and the mapping is: ICMPX reason | ICMPv6 | ICMPv4 | | admin-prohibited | admin-prohibited | admin-prohibited port-unreachable | port-unreachable | port-unreachable no-route | no-route | net-unreachable host-unreachable | addr-unreachable | host-unreachable Signed-off-by: Alvaro Neira Ayuso <alvaroneay@gmail.com> Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
-rw-r--r--include/datatype.h9
-rw-r--r--include/linux/netfilter/nf_tables.h21
-rw-r--r--include/proto.h1
-rw-r--r--include/statement.h3
-rw-r--r--src/datatype.c105
-rw-r--r--src/evaluate.c184
-rw-r--r--src/netlink_delinearize.c67
-rw-r--r--src/netlink_linearize.c5
-rw-r--r--src/parser.y54
-rw-r--r--src/payload.c31
-rw-r--r--src/scanner.l3
-rw-r--r--src/statement.c31
12 files changed, 504 insertions, 10 deletions
diff --git a/include/datatype.h b/include/datatype.h
index 51822637..15fea441 100644
--- a/include/datatype.h
+++ b/include/datatype.h
@@ -36,6 +36,9 @@
* @TYPE_ICMP6_TYPE: ICMPv6 type codes (integer subtype)
* @TYPE_CT_LABEL: Conntrack Label (bitmask subtype)
* @TYPE_PKTTYPE: packet type (integer subtype)
+ * @TYPE_ICMP_CODE: icmp code (integer subtype)
+ * @TYPE_ICMPV6_CODE: icmpv6 code (integer subtype)
+ * @TYPE_ICMPX_CODE: icmpx code (integer subtype)
*/
enum datatypes {
TYPE_INVALID,
@@ -70,6 +73,9 @@ enum datatypes {
TYPE_ICMP6_TYPE,
TYPE_CT_LABEL,
TYPE_PKTTYPE,
+ TYPE_ICMP_CODE,
+ TYPE_ICMPV6_CODE,
+ TYPE_ICMPX_CODE,
__TYPE_MAX
};
#define TYPE_MAX (__TYPE_MAX - 1)
@@ -194,6 +200,9 @@ extern const struct datatype arphrd_type;
extern const struct datatype inet_protocol_type;
extern const struct datatype inet_service_type;
extern const struct datatype mark_type;
+extern const struct datatype icmp_code_type;
+extern const struct datatype icmpv6_code_type;
+extern const struct datatype icmpx_code_type;
extern const struct datatype time_type;
extern const struct datatype *concat_type_alloc(const struct expr *expr);
diff --git a/include/linux/netfilter/nf_tables.h b/include/linux/netfilter/nf_tables.h
index b72ccfea..f04d997a 100644
--- a/include/linux/netfilter/nf_tables.h
+++ b/include/linux/netfilter/nf_tables.h
@@ -749,13 +749,34 @@ enum nft_queue_attributes {
*
* @NFT_REJECT_ICMP_UNREACH: reject using ICMP unreachable
* @NFT_REJECT_TCP_RST: reject using TCP RST
+ * @NFT_REJECT_ICMPX_UNREACH: abstracted ICMP unreachable for bridge and inet
*/
enum nft_reject_types {
NFT_REJECT_ICMP_UNREACH,
NFT_REJECT_TCP_RST,
+ NFT_REJECT_ICMPX_UNREACH,
};
/**
+ * enum nft_reject_code - Abstracted reject codes
+ *
+ * @NFT_REJECT_ICMPX_NO_ROUTE: no route to host - network unreachable
+ * @NFT_REJECT_ICMPX_PORT_UNREACH: port unreachable
+ * @NFT_REJECT_ICMPX_HOST_UNREACH: host unreachable
+ * @NFT_REJECT_ICMPX_ADMIN_PROHIBITED: administratevely prohibited
+ *
+ * These codes are mapped to real ICMP and ICMPv6 codes.
+ */
+enum nft_reject_inet_code {
+ NFT_REJECT_ICMPX_NO_ROUTE = 0,
+ NFT_REJECT_ICMPX_PORT_UNREACH,
+ NFT_REJECT_ICMPX_HOST_UNREACH,
+ NFT_REJECT_ICMPX_ADMIN_PROHIBITED,
+ __NFT_REJECT_ICMPX_MAX
+};
+#define NFT_REJECT_ICMPX_MAX (__NFT_REJECT_ICMPX_MAX + 1)
+
+/**
* enum nft_reject_attributes - nf_tables reject expression netlink attributes
*
* @NFTA_REJECT_TYPE: packet type to use (NLA_U32: nft_reject_types)
diff --git a/include/proto.h b/include/proto.h
index cc1f51f0..0e531b24 100644
--- a/include/proto.h
+++ b/include/proto.h
@@ -252,6 +252,7 @@ enum udp_hdr_fields {
enum tcp_hdr_fields {
TCPHDR_INVALID,
+ TCPHDR_UNSPEC = TCPHDR_INVALID,
TCPHDR_SPORT,
TCPHDR_DPORT,
TCPHDR_SEQ,
diff --git a/include/statement.h b/include/statement.h
index 7a57f7dd..574835cd 100644
--- a/include/statement.h
+++ b/include/statement.h
@@ -56,7 +56,10 @@ struct limit_stmt {
extern struct stmt *limit_stmt_alloc(const struct location *loc);
struct reject_stmt {
+ struct expr *expr;
enum nft_reject_types type;
+ int8_t icmp_code;
+ unsigned int family;
};
extern struct stmt *reject_stmt_alloc(const struct location *loc);
diff --git a/src/datatype.c b/src/datatype.c
index 7090b0ba..8ad211c1 100644
--- a/src/datatype.c
+++ b/src/datatype.c
@@ -24,6 +24,9 @@
#include <gmputil.h>
#include <erec.h>
+#include <netinet/ip_icmp.h>
+#include <netinet/icmp6.h>
+
static const struct datatype *datatypes[TYPE_MAX + 1] = {
[TYPE_INVALID] = &invalid_type,
[TYPE_VERDICT] = &verdict_type,
@@ -41,6 +44,9 @@ static const struct datatype *datatypes[TYPE_MAX + 1] = {
[TYPE_TIME] = &time_type,
[TYPE_MARK] = &mark_type,
[TYPE_ARPHRD] = &arphrd_type,
+ [TYPE_ICMP_CODE] = &icmp_code_type,
+ [TYPE_ICMPV6_CODE] = &icmpv6_code_type,
+ [TYPE_ICMPX_CODE] = &icmpx_code_type,
};
void datatype_register(const struct datatype *dtype)
@@ -685,6 +691,105 @@ const struct datatype mark_type = {
.flags = DTYPE_F_PREFIX,
};
+static const struct symbol_table icmp_code_tbl = {
+ .symbols = {
+ SYMBOL("net-unreachable", ICMP_NET_UNREACH),
+ SYMBOL("host-unreachable", ICMP_HOST_UNREACH),
+ SYMBOL("prot-unreachable", ICMP_PROT_UNREACH),
+ SYMBOL("port-unreachable", ICMP_PORT_UNREACH),
+ SYMBOL("net-prohibited", ICMP_NET_ANO),
+ SYMBOL("host-prohibited", ICMP_HOST_ANO),
+ SYMBOL("admin-prohibited", ICMP_PKT_FILTERED),
+ SYMBOL_LIST_END
+ },
+};
+
+static void icmp_code_type_print(const struct expr *expr)
+{
+ return symbolic_constant_print(&icmp_code_tbl, expr);
+}
+
+static struct error_record *icmp_code_type_parse(const struct expr *sym,
+ struct expr **res)
+{
+ return symbolic_constant_parse(sym, &icmp_code_tbl, res);
+}
+
+const struct datatype icmp_code_type = {
+ .type = TYPE_ICMP_CODE,
+ .name = "icmp code",
+ .desc = "icmp code",
+ .size = BITS_PER_BYTE,
+ .byteorder = BYTEORDER_BIG_ENDIAN,
+ .basetype = &integer_type,
+ .print = icmp_code_type_print,
+ .parse = icmp_code_type_parse,
+};
+
+static const struct symbol_table icmpv6_code_tbl = {
+ .symbols = {
+ SYMBOL("no-route", ICMP6_DST_UNREACH_NOROUTE),
+ SYMBOL("admin-prohibited", ICMP6_DST_UNREACH_ADMIN),
+ SYMBOL("addr-unreachable", ICMP6_DST_UNREACH_ADDR),
+ SYMBOL("port-unreachable", ICMP6_DST_UNREACH_NOPORT),
+ SYMBOL_LIST_END
+ },
+};
+
+static void icmpv6_code_type_print(const struct expr *expr)
+{
+ return symbolic_constant_print(&icmpv6_code_tbl, expr);
+}
+
+static struct error_record *icmpv6_code_type_parse(const struct expr *sym,
+ struct expr **res)
+{
+ return symbolic_constant_parse(sym, &icmpv6_code_tbl, res);
+}
+
+const struct datatype icmpv6_code_type = {
+ .type = TYPE_ICMPV6_CODE,
+ .name = "icmpv6 code",
+ .desc = "icmpv6 code",
+ .size = BITS_PER_BYTE,
+ .byteorder = BYTEORDER_BIG_ENDIAN,
+ .basetype = &integer_type,
+ .print = icmpv6_code_type_print,
+ .parse = icmpv6_code_type_parse,
+};
+
+static const struct symbol_table icmpx_code_tbl = {
+ .symbols = {
+ SYMBOL("port-unreachable", NFT_REJECT_ICMPX_PORT_UNREACH),
+ SYMBOL("admin-prohibited", NFT_REJECT_ICMPX_ADMIN_PROHIBITED),
+ SYMBOL("no-route", NFT_REJECT_ICMPX_NO_ROUTE),
+ SYMBOL("host-unreachable", NFT_REJECT_ICMPX_HOST_UNREACH),
+ SYMBOL_LIST_END
+ },
+};
+
+static void icmpx_code_type_print(const struct expr *expr)
+{
+ return symbolic_constant_print(&icmpx_code_tbl, expr);
+}
+
+static struct error_record *icmpx_code_type_parse(const struct expr *sym,
+ struct expr **res)
+{
+ return symbolic_constant_parse(sym, &icmpx_code_tbl, res);
+}
+
+const struct datatype icmpx_code_type = {
+ .type = TYPE_ICMPX_CODE,
+ .name = "icmpx code",
+ .desc = "icmpx code",
+ .size = BITS_PER_BYTE,
+ .byteorder = BYTEORDER_BIG_ENDIAN,
+ .basetype = &integer_type,
+ .print = icmpx_code_type_print,
+ .parse = icmpx_code_type_parse,
+};
+
static void time_type_print(const struct expr *expr)
{
uint64_t days, hours, minutes, seconds;
diff --git a/src/evaluate.c b/src/evaluate.c
index 52ce5487..602148c8 100644
--- a/src/evaluate.c
+++ b/src/evaluate.c
@@ -17,6 +17,8 @@
#include <linux/netfilter.h>
#include <linux/netfilter_arp.h>
#include <linux/netfilter/nf_tables.h>
+#include <netinet/ip_icmp.h>
+#include <netinet/icmp6.h>
#include <expression.h>
#include <statement.h>
@@ -1126,12 +1128,192 @@ static int stmt_evaluate_meta(struct eval_ctx *ctx, struct stmt *stmt)
return 0;
}
-static int stmt_evaluate_reject(struct eval_ctx *ctx, struct stmt *stmt)
+static int reject_payload_gen_dependency_tcp(struct eval_ctx *ctx,
+ struct stmt *stmt,
+ struct expr **payload)
+{
+ const struct proto_desc *desc;
+
+ desc = ctx->pctx.protocol[PROTO_BASE_TRANSPORT_HDR].desc;
+ if (desc != NULL)
+ return 0;
+ *payload = payload_expr_alloc(&stmt->location, &proto_tcp,
+ TCPHDR_UNSPEC);
+ return 0;
+}
+
+static int reject_payload_gen_dependency_family(struct eval_ctx *ctx,
+ struct stmt *stmt,
+ struct expr **payload)
+{
+ const struct proto_desc *base;
+
+ base = ctx->pctx.protocol[PROTO_BASE_NETWORK_HDR].desc;
+ if (base != NULL)
+ return 0;
+
+ if (stmt->reject.icmp_code < 0)
+ return stmt_error(ctx, stmt, "missing icmp error type");
+
+ /* Generate a network dependency */
+ switch (stmt->reject.family) {
+ case NFPROTO_IPV4:
+ *payload = payload_expr_alloc(&stmt->location, &proto_ip,
+ IPHDR_PROTOCOL);
+ break;
+ case NFPROTO_IPV6:
+ *payload = payload_expr_alloc(&stmt->location, &proto_ip6,
+ IP6HDR_NEXTHDR);
+ break;
+ default:
+ BUG("unknown reject family");
+ }
+ return 0;
+}
+
+static int stmt_reject_gen_dependency(struct eval_ctx *ctx, struct stmt *stmt,
+ struct expr *expr)
+{
+ struct expr *payload = NULL;
+ struct stmt *nstmt;
+
+ switch (stmt->reject.type) {
+ case NFT_REJECT_TCP_RST:
+ if (reject_payload_gen_dependency_tcp(ctx, stmt, &payload) < 0)
+ return -1;
+ break;
+ case NFT_REJECT_ICMP_UNREACH:
+ if (reject_payload_gen_dependency_family(ctx, stmt,
+ &payload) < 0)
+ return -1;
+ break;
+ default:
+ BUG("cannot generate reject dependency for type %d",
+ stmt->reject.type);
+ }
+
+ if (payload_gen_dependency(ctx, payload, &nstmt) < 0)
+ return -1;
+
+ list_add(&nstmt->list, &ctx->cmd->rule->stmts);
+ return 0;
+}
+
+static int stmt_evaluate_reject_family(struct eval_ctx *ctx, struct stmt *stmt,
+ struct expr *expr)
{
+ switch (ctx->pctx.family) {
+ case NFPROTO_ARP:
+ return stmt_error(ctx, stmt, "cannot use reject with arp");
+ case NFPROTO_IPV4:
+ case NFPROTO_IPV6:
+ switch (stmt->reject.type) {
+ case NFT_REJECT_TCP_RST:
+ if (stmt_reject_gen_dependency(ctx, stmt, expr) < 0)
+ return -1;
+ break;
+ case NFT_REJECT_ICMPX_UNREACH:
+ return stmt_error(ctx, stmt,
+ "abstracted ICMP unreachable not supported");
+ case NFT_REJECT_ICMP_UNREACH:
+ if (stmt->reject.family != ctx->pctx.family)
+ return stmt_error(ctx, stmt,
+ "conflicting protocols specified: ip vs ip6");
+ break;
+ }
+ break;
+ case NFPROTO_INET:
+ case NFPROTO_BRIDGE:
+ if (stmt->reject.type == NFT_REJECT_ICMPX_UNREACH)
+ break;
+ if (stmt_reject_gen_dependency(ctx, stmt, expr) < 0)
+ return -1;
+ break;
+ }
+
stmt->flags |= STMT_F_TERMINAL;
return 0;
}
+static int stmt_evaluate_reject_default(struct eval_ctx *ctx,
+ struct stmt *stmt)
+{
+ switch (ctx->pctx.family) {
+ case NFPROTO_IPV4:
+ case NFPROTO_IPV6:
+ stmt->reject.type = NFT_REJECT_ICMP_UNREACH;
+ stmt->reject.family = ctx->pctx.family;
+ if (ctx->pctx.family == NFPROTO_IPV4)
+ stmt->reject.icmp_code = ICMP_PORT_UNREACH;
+ else
+ stmt->reject.icmp_code = ICMP6_DST_UNREACH_NOPORT;
+ break;
+ case NFPROTO_INET:
+ case NFPROTO_BRIDGE:
+ stmt->reject.type = NFT_REJECT_ICMPX_UNREACH;
+ stmt->reject.icmp_code = NFT_REJECT_ICMPX_PORT_UNREACH;
+ break;
+ }
+ return 0;
+}
+
+static int stmt_evaluate_reject_icmp(struct eval_ctx *ctx, struct stmt *stmt)
+{
+ struct error_record *erec;
+ struct expr *code;
+
+ erec = symbol_parse(stmt->reject.expr, &code);
+ if (erec != NULL) {
+ erec_queue(erec, ctx->msgs);
+ return -1;
+ }
+ stmt->reject.icmp_code = mpz_get_uint8(code->value);
+ return 0;
+}
+
+static int stmt_evaluate_reset(struct eval_ctx *ctx, struct stmt *stmt)
+{
+ int protonum;
+ const struct proto_desc *desc, *base;
+ struct proto_ctx *pctx = &ctx->pctx;
+
+ base = pctx->protocol[PROTO_BASE_NETWORK_HDR].desc;
+ desc = pctx->protocol[PROTO_BASE_TRANSPORT_HDR].desc;
+ if (desc == NULL)
+ return 0;
+
+ protonum = proto_find_num(base, desc);
+ switch (protonum) {
+ case IPPROTO_TCP:
+ break;
+ default:
+ if (stmt->reject.type == NFT_REJECT_TCP_RST) {
+ return stmt_error(ctx, stmt,
+ "you cannot use tcp reset with this protocol");
+ }
+ break;
+ }
+ return 0;
+}
+
+static int stmt_evaluate_reject(struct eval_ctx *ctx, struct stmt *stmt)
+{
+ struct expr *expr = ctx->cmd->expr;
+
+ if (stmt->reject.icmp_code < 0) {
+ if (stmt_evaluate_reject_default(ctx, stmt) < 0)
+ return -1;
+ } else if (stmt->reject.expr != NULL) {
+ if (stmt_evaluate_reject_icmp(ctx, stmt) < 0)
+ return -1;
+ } else {
+ if (stmt_evaluate_reset(ctx, stmt) < 0)
+ return -1;
+ }
+
+ return stmt_evaluate_reject_family(ctx, stmt, expr);
+}
+
static int stmt_evaluate_nat(struct eval_ctx *ctx, struct stmt *stmt)
{
struct proto_ctx *pctx = &ctx->pctx;
diff --git a/src/netlink_delinearize.c b/src/netlink_delinearize.c
index 796b6327..397b65c0 100644
--- a/src/netlink_delinearize.c
+++ b/src/netlink_delinearize.c
@@ -14,6 +14,9 @@
#include <string.h>
#include <limits.h>
#include <linux/netfilter/nf_tables.h>
+#include <arpa/inet.h>
+#include <linux/netfilter.h>
+#include <net/ethernet.h>
#include <netlink.h>
#include <rule.h>
#include <statement.h>
@@ -472,8 +475,15 @@ static void netlink_parse_reject(struct netlink_parse_ctx *ctx,
const struct nft_rule_expr *expr)
{
struct stmt *stmt;
+ uint8_t icmp_code;
stmt = reject_stmt_alloc(loc);
+ stmt->reject.type = nft_rule_expr_get_u32(expr, NFT_EXPR_REJECT_TYPE);
+ icmp_code = nft_rule_expr_get_u8(expr, NFT_EXPR_REJECT_CODE);
+ stmt->reject.icmp_code = icmp_code;
+ stmt->reject.expr = constant_expr_alloc(loc, &integer_type,
+ BYTEORDER_HOST_ENDIAN, 8,
+ &icmp_code);
list_add_tail(&stmt->list, &ctx->rule->stmts);
}
@@ -899,6 +909,60 @@ static void expr_postprocess(struct rule_pp_ctx *ctx,
}
}
+static void stmt_reject_postprocess(struct rule_pp_ctx rctx, struct stmt *stmt)
+{
+ const struct proto_desc *desc, *base;
+ int protocol;
+
+ switch (rctx.pctx.family) {
+ case NFPROTO_IPV4:
+ stmt->reject.family = rctx.pctx.family;
+ stmt->reject.expr->dtype = &icmp_code_type;
+ break;
+ case NFPROTO_IPV6:
+ stmt->reject.family = rctx.pctx.family;
+ stmt->reject.expr->dtype = &icmpv6_code_type;
+ break;
+ case NFPROTO_INET:
+ if (stmt->reject.type == NFT_REJECT_ICMPX_UNREACH)
+ break;
+ base = rctx.pctx.protocol[PROTO_BASE_LL_HDR].desc;
+ desc = rctx.pctx.protocol[PROTO_BASE_NETWORK_HDR].desc;
+ protocol = proto_find_num(base, desc);
+ switch (protocol) {
+ case NFPROTO_IPV4:
+ stmt->reject.expr->dtype = &icmp_code_type;
+ break;
+ case NFPROTO_IPV6:
+ stmt->reject.expr->dtype = &icmpv6_code_type;
+ break;
+ }
+ stmt->reject.family = protocol;
+ break;
+ case NFPROTO_BRIDGE:
+ if (stmt->reject.type == NFT_REJECT_ICMPX_UNREACH)
+ break;
+ base = rctx.pctx.protocol[PROTO_BASE_LL_HDR].desc;
+ desc = rctx.pctx.protocol[PROTO_BASE_NETWORK_HDR].desc;
+ protocol = proto_find_num(base, desc);
+ switch (protocol) {
+ case __constant_htons(ETH_P_IP):
+ stmt->reject.family = NFPROTO_IPV4;
+ stmt->reject.expr->dtype = &icmp_code_type;
+ break;
+ case __constant_htons(ETH_P_IPV6):
+ stmt->reject.family = NFPROTO_IPV6;
+ stmt->reject.expr->dtype = &icmpv6_code_type;
+ break;
+ default:
+ break;
+ }
+ break;
+ default:
+ break;
+ }
+}
+
static void rule_parse_postprocess(struct netlink_parse_ctx *ctx, struct rule *rule)
{
struct rule_pp_ctx rctx;
@@ -926,6 +990,9 @@ static void rule_parse_postprocess(struct netlink_parse_ctx *ctx, struct rule *r
if (stmt->nat.proto != NULL)
expr_postprocess(&rctx, stmt, &stmt->nat.proto);
break;
+ case STMT_REJECT:
+ stmt_reject_postprocess(rctx, stmt);
+ break;
default:
break;
}
diff --git a/src/netlink_linearize.c b/src/netlink_linearize.c
index c46b6d47..29f8e9ae 100644
--- a/src/netlink_linearize.c
+++ b/src/netlink_linearize.c
@@ -612,7 +612,10 @@ static void netlink_gen_reject_stmt(struct netlink_linearize_ctx *ctx,
nle = alloc_nft_expr("reject");
nft_rule_expr_set_u32(nle, NFT_EXPR_REJECT_TYPE, stmt->reject.type);
- nft_rule_expr_set_u8(nle, NFT_EXPR_REJECT_CODE, 0);
+ if (stmt->reject.icmp_code != -1)
+ nft_rule_expr_set_u8(nle, NFT_EXPR_REJECT_CODE,
+ stmt->reject.icmp_code);
+
nft_rule_add_expr(ctx->nlr, nle);
}
diff --git a/src/parser.y b/src/parser.y
index 4a8df7b7..03d6d138 100644
--- a/src/parser.y
+++ b/src/parser.y
@@ -19,6 +19,8 @@
#include <linux/netfilter.h>
#include <linux/netfilter/nf_tables.h>
#include <linux/netfilter/nf_conntrack_tuple_common.h>
+#include <netinet/ip_icmp.h>
+#include <netinet/icmp6.h>
#include <libnftnl/common.h>
#include <libnftnl/set.h>
@@ -368,6 +370,9 @@ static void location_update(struct location *loc, struct location *rhs, int n)
%token WEEK "week"
%token _REJECT "reject"
+%token RESET "reset"
+%token WITH "with"
+%token ICMPX "icmpx"
%token SNAT "snat"
%token DNAT "dnat"
@@ -431,8 +436,8 @@ static void location_update(struct location *loc, struct location *rhs, int n)
%type <stmt> limit_stmt
%destructor { stmt_free($$); } limit_stmt
%type <val> time_unit
-%type <stmt> reject_stmt
-%destructor { stmt_free($$); } reject_stmt
+%type <stmt> reject_stmt reject_stmt_alloc
+%destructor { stmt_free($$); } reject_stmt reject_stmt_alloc
%type <stmt> nat_stmt nat_stmt_alloc
%destructor { stmt_free($$); } nat_stmt nat_stmt_alloc
%type <stmt> queue_stmt queue_stmt_alloc
@@ -1374,12 +1379,55 @@ time_unit : SECOND { $$ = 1ULL; }
| WEEK { $$ = 1ULL * 60 * 60 * 24 * 7; }
;
-reject_stmt : _REJECT
+reject_stmt : reject_stmt_alloc reject_opts
+ ;
+
+reject_stmt_alloc : _REJECT
{
$$ = reject_stmt_alloc(&@$);
}
;
+reject_opts : /* empty */
+ {
+ $<stmt>0->reject.type = -1;
+ $<stmt>0->reject.icmp_code = -1;
+ }
+ | WITH ICMP TYPE STRING
+ {
+ $<stmt>0->reject.family = NFPROTO_IPV4;
+ $<stmt>0->reject.type = NFT_REJECT_ICMP_UNREACH;
+ $<stmt>0->reject.expr =
+ symbol_expr_alloc(&@$, SYMBOL_VALUE,
+ current_scope(state),
+ $4);
+ $<stmt>0->reject.expr->dtype = &icmp_code_type;
+ }
+ | WITH ICMP6 TYPE STRING
+ {
+ $<stmt>0->reject.family = NFPROTO_IPV6;
+ $<stmt>0->reject.type = NFT_REJECT_ICMP_UNREACH;
+ $<stmt>0->reject.expr =
+ symbol_expr_alloc(&@$, SYMBOL_VALUE,
+ current_scope(state),
+ $4);
+ $<stmt>0->reject.expr->dtype = &icmpv6_code_type;
+ }
+ | WITH ICMPX TYPE STRING
+ {
+ $<stmt>0->reject.type = NFT_REJECT_ICMPX_UNREACH;
+ $<stmt>0->reject.expr =
+ symbol_expr_alloc(&@$, SYMBOL_VALUE,
+ current_scope(state),
+ $4);
+ $<stmt>0->reject.expr->dtype = &icmpx_code_type;
+ }
+ | WITH TCP RESET
+ {
+ $<stmt>0->reject.type = NFT_REJECT_TCP_RST;
+ }
+ ;
+
nat_stmt : nat_stmt_alloc nat_stmt_args
;
diff --git a/src/payload.c b/src/payload.c
index b7b74edd..ebf8079b 100644
--- a/src/payload.c
+++ b/src/payload.c
@@ -198,11 +198,32 @@ int payload_gen_dependency(struct eval_ctx *ctx, const struct expr *expr,
}
desc = ctx->pctx.protocol[expr->payload.base - 1].desc;
- /* Special case for mixed IPv4/IPv6 tables: use meta L4 proto */
- if (desc == NULL &&
- ctx->pctx.family == NFPROTO_INET &&
- expr->payload.base == PROTO_BASE_TRANSPORT_HDR)
- desc = &proto_inet_service;
+ /* Special case for mixed IPv4/IPv6 and bridge tables */
+ if (desc == NULL) {
+ switch (ctx->pctx.family) {
+ case NFPROTO_INET:
+ switch (expr->payload.base) {
+ case PROTO_BASE_TRANSPORT_HDR:
+ desc = &proto_inet_service;
+ break;
+ case PROTO_BASE_LL_HDR:
+ desc = &proto_inet;
+ break;
+ default:
+ break;
+ }
+ break;
+ case NFPROTO_BRIDGE:
+ switch (expr->payload.base) {
+ case PROTO_BASE_LL_HDR:
+ desc = &proto_eth;
+ break;
+ default:
+ break;
+ }
+ break;
+ }
+ }
if (desc == NULL)
return expr_error(ctx->msgs, expr,
diff --git a/src/scanner.l b/src/scanner.l
index 35c9446f..9f054fdf 100644
--- a/src/scanner.l
+++ b/src/scanner.l
@@ -313,6 +313,9 @@ addrstring ({macaddr}|{ip4addr}|{ip6addr})
"week" { return WEEK; }
"reject" { return _REJECT; }
+"with" { return WITH; }
+"reset" { return RESET; }
+"icmpx" { return ICMPX; }
"snat" { return SNAT; }
"dnat" { return DNAT; }
diff --git a/src/statement.c b/src/statement.c
index 8e4b49e2..357f0948 100644
--- a/src/statement.c
+++ b/src/statement.c
@@ -16,6 +16,10 @@
#include <string.h>
#include <syslog.h>
+#include <arpa/inet.h>
+#include <linux/netfilter.h>
+#include <netinet/ip_icmp.h>
+#include <netinet/icmp6.h>
#include <statement.h>
#include <utils.h>
#include <list.h>
@@ -227,6 +231,33 @@ struct stmt *queue_stmt_alloc(const struct location *loc)
static void reject_stmt_print(const struct stmt *stmt)
{
printf("reject");
+ switch (stmt->reject.type) {
+ case NFT_REJECT_TCP_RST:
+ printf(" with tcp reset");
+ break;
+ case NFT_REJECT_ICMPX_UNREACH:
+ if (stmt->reject.icmp_code == NFT_REJECT_ICMPX_PORT_UNREACH)
+ break;
+ printf(" with icmpx type ");
+ expr_print(stmt->reject.expr);
+ break;
+ case NFT_REJECT_ICMP_UNREACH:
+ switch (stmt->reject.family) {
+ case NFPROTO_IPV4:
+ if (stmt->reject.icmp_code == ICMP_PORT_UNREACH)
+ break;
+ printf(" with icmp type ");
+ expr_print(stmt->reject.expr);
+ break;
+ case NFPROTO_IPV6:
+ if (stmt->reject.icmp_code == ICMP6_DST_UNREACH_NOPORT)
+ break;
+ printf(" with icmpv6 type ");
+ expr_print(stmt->reject.expr);
+ break;
+ }
+ break;
+ }
}
static const struct stmt_ops reject_stmt_ops = {