summaryrefslogtreecommitdiffstats
path: root/src
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 /src
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>
Diffstat (limited to 'src')
-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
8 files changed, 470 insertions, 10 deletions
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 = {