netfilter-devel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [nft PATCH 1/4 v2] payload: generate dependency in the appropriate byteorder
@ 2014-09-26 16:50 Alvaro Neira Ayuso
  2014-09-26 16:50 ` [nft PATCH 2/4 v2] src: Enhance payload_gen_dependency() Alvaro Neira Ayuso
                   ` (2 more replies)
  0 siblings, 3 replies; 7+ messages in thread
From: Alvaro Neira Ayuso @ 2014-09-26 16:50 UTC (permalink / raw)
  To: netfilter-devel; +Cc: kaber

If we add a dependency, the constant expression on the right
hand side must be represented in the appropriate order.

Example without this patch:

  nft add rule bridge filter input reject with icmp-host-unreach --debug netlink

  [ payload load 2b @ link header + 12 => reg 1 ]
  [ cmp eq reg 1 0x00000800 ]
  [ reject type 0 code 1 ]

When we create the payload expression we have the right value in host endian but
this has to be in big endian.

With this patch, if we add the same rule:

  nft add rule bridge filter input reject with icmp-host-unreach --debug netlink

  [ payload load 2b @ link header + 12 => reg 1 ]
  [ cmp eq reg 1 0x00000008 ]
  [ reject type 0 code 1 ]

The new dependency is converted to big endian.

Signed-off-by: Alvaro Neira Ayuso <alvaroneay@gmail.com>
---
[Changes in v2]
* Added a example with the problem and the fix in the description

 src/payload.c |    3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)

diff --git a/src/payload.c b/src/payload.c
index 1eee4e0..a3bbe51 100644
--- a/src/payload.c
+++ b/src/payload.c
@@ -216,8 +216,7 @@ int payload_gen_dependency(struct eval_ctx *ctx, const struct expr *expr,
 		left = payload_expr_alloc(&expr->location, desc, desc->protocol_key);
 
 	right = constant_expr_alloc(&expr->location, tmpl->dtype,
-				    BYTEORDER_HOST_ENDIAN,
-				    tmpl->len,
+				    tmpl->dtype->byteorder, tmpl->len,
 				    constant_data_ptr(protocol, tmpl->len));
 
 	dep = relational_expr_alloc(&expr->location, OP_EQ, left, right);
-- 
1.7.10.4


^ permalink raw reply related	[flat|nested] 7+ messages in thread

* [nft PATCH 2/4 v2] src: Enhance payload_gen_dependency()
  2014-09-26 16:50 [nft PATCH 1/4 v2] payload: generate dependency in the appropriate byteorder Alvaro Neira Ayuso
@ 2014-09-26 16:50 ` Alvaro Neira Ayuso
  2014-09-26 16:50 ` [nft PATCH 3/4 v2] datatype: Enhance symbolic_constant_parse() Alvaro Neira Ayuso
  2014-09-26 16:50 ` [nft PATCH 4/4 v4] nft: complete reject support Alvaro Neira Ayuso
  2 siblings, 0 replies; 7+ messages in thread
From: Alvaro Neira Ayuso @ 2014-09-26 16:50 UTC (permalink / raw)
  To: netfilter-devel; +Cc: kaber

With this patch, this function returns a statement with the new dependency
that we want to add, instead of an expression.

This change is needed in a follow up patch.

Signed-off-by: Alvaro Neira Ayuso <alvaroneay@gmail.com>
---
[No changes in v2]

 include/payload.h   |    3 ++-
 include/statement.h |    1 +
 src/evaluate.c      |    9 ++-------
 src/payload.c       |   18 +++++++++++++++---
 4 files changed, 20 insertions(+), 11 deletions(-)

diff --git a/include/payload.h b/include/payload.h
index d47e564..95364af 100644
--- a/include/payload.h
+++ b/include/payload.h
@@ -11,8 +11,9 @@ extern void payload_init_raw(struct expr *expr, enum proto_bases base,
 			     unsigned int offset, unsigned int len);
 
 struct eval_ctx;
+struct stmt;
 extern int payload_gen_dependency(struct eval_ctx *ctx, const struct expr *expr,
-				  struct expr **res);
+				  struct stmt **res);
 
 extern bool payload_is_adjacent(const struct expr *e1, const struct expr *e2);
 extern struct expr *payload_expr_join(const struct expr *e1,
diff --git a/include/statement.h b/include/statement.h
index e2f02b8..7a57f7d 100644
--- a/include/statement.h
+++ b/include/statement.h
@@ -166,6 +166,7 @@ struct stmt {
 
 extern struct stmt *stmt_alloc(const struct location *loc,
 			       const struct stmt_ops *ops);
+int stmt_evaluate(struct eval_ctx *ctx, struct stmt *stmt);
 extern void stmt_free(struct stmt *stmt);
 extern void stmt_list_free(struct list_head *list);
 extern void stmt_print(const struct stmt *stmt);
diff --git a/src/evaluate.c b/src/evaluate.c
index 284ee72..52ce548 100644
--- a/src/evaluate.c
+++ b/src/evaluate.c
@@ -26,7 +26,6 @@
 #include <utils.h>
 
 static int expr_evaluate(struct eval_ctx *ctx, struct expr **expr);
-static int stmt_evaluate(struct eval_ctx *ctx, struct stmt *stmt);
 
 static const char *byteorder_names[] = {
 	[BYTEORDER_INVALID]		= "invalid",
@@ -271,13 +270,9 @@ static int expr_evaluate_payload(struct eval_ctx *ctx, struct expr **expr)
 	struct expr *payload = *expr;
 	enum proto_bases base = payload->payload.base;
 	struct stmt *nstmt;
-	struct expr *nexpr;
 
 	if (ctx->pctx.protocol[base].desc == NULL) {
-		if (payload_gen_dependency(ctx, payload, &nexpr) < 0)
-			return -1;
-		nstmt = expr_stmt_alloc(&nexpr->location, nexpr);
-		if (stmt_evaluate(ctx, nstmt) < 0)
+		if (payload_gen_dependency(ctx, payload, &nstmt) < 0)
 			return -1;
 		list_add_tail(&nstmt->list, &ctx->stmt->list);
 	} else if (ctx->pctx.protocol[base].desc != payload->payload.desc)
@@ -1205,7 +1200,7 @@ static int stmt_evaluate_log(struct eval_ctx *ctx, struct stmt *stmt)
 	return 0;
 }
 
-static int stmt_evaluate(struct eval_ctx *ctx, struct stmt *stmt)
+int stmt_evaluate(struct eval_ctx *ctx, struct stmt *stmt)
 {
 #ifdef DEBUG
 	if (debug_level & DEBUG_EVALUATION) {
diff --git a/src/payload.c b/src/payload.c
index a3bbe51..b7b74ed 100644
--- a/src/payload.c
+++ b/src/payload.c
@@ -21,6 +21,7 @@
 
 #include <rule.h>
 #include <expression.h>
+#include <statement.h>
 #include <payload.h>
 #include <gmputil.h>
 #include <utils.h>
@@ -160,12 +161,13 @@ void payload_init_raw(struct expr *expr, enum proto_bases base,
  *   in the input path though.
  */
 int payload_gen_dependency(struct eval_ctx *ctx, const struct expr *expr,
-			   struct expr **res)
+			   struct stmt **res)
 {
 	const struct hook_proto_desc *h = &hook_proto_desc[ctx->pctx.family];
 	const struct proto_desc *desc;
 	const struct proto_hdr_template *tmpl;
 	struct expr *dep, *left, *right;
+	struct stmt *stmt;
 	int protocol;
 	uint16_t type;
 
@@ -186,7 +188,12 @@ int payload_gen_dependency(struct eval_ctx *ctx, const struct expr *expr,
 					    2 * BITS_PER_BYTE, &type);
 
 		dep = relational_expr_alloc(&expr->location, OP_EQ, left, right);
-		*res = dep;
+		stmt = expr_stmt_alloc(&dep->location, dep);
+		if (stmt_evaluate(ctx, stmt) < 0) {
+			return expr_error(ctx->msgs, expr,
+					  "dependency statement is invalid");
+		}
+		*res = stmt;
 		return 0;
 	}
 
@@ -220,8 +227,13 @@ int payload_gen_dependency(struct eval_ctx *ctx, const struct expr *expr,
 				    constant_data_ptr(protocol, tmpl->len));
 
 	dep = relational_expr_alloc(&expr->location, OP_EQ, left, right);
+	stmt = expr_stmt_alloc(&dep->location, dep);
+	if (stmt_evaluate(ctx, stmt) < 0) {
+		return expr_error(ctx->msgs, expr,
+					  "dependency statement is invalid");
+	}
 	left->ops->pctx_update(&ctx->pctx, dep);
-	*res = dep;
+	*res = stmt;
 	return 0;
 }
 
-- 
1.7.10.4


^ permalink raw reply related	[flat|nested] 7+ messages in thread

* [nft PATCH 3/4 v2] datatype: Enhance symbolic_constant_parse()
  2014-09-26 16:50 [nft PATCH 1/4 v2] payload: generate dependency in the appropriate byteorder Alvaro Neira Ayuso
  2014-09-26 16:50 ` [nft PATCH 2/4 v2] src: Enhance payload_gen_dependency() Alvaro Neira Ayuso
@ 2014-09-26 16:50 ` Alvaro Neira Ayuso
  2014-09-26 16:50 ` [nft PATCH 4/4 v4] nft: complete reject support Alvaro Neira Ayuso
  2 siblings, 0 replies; 7+ messages in thread
From: Alvaro Neira Ayuso @ 2014-09-26 16:50 UTC (permalink / raw)
  To: netfilter-devel; +Cc: kaber

With this patch, this function finds the symbol inside the table. If the symbol
doesn't exist we use the basetype to parse it and create the constant
expression. Otherwise, return an error message.

This a refactorization to reuse this code in a follow up patch.

Signed-off-by: Alvaro Neira Ayuso <alvaroneay@gmail.com>
---
[Changes in v2]
* Removed the function symbolic_constant_parse_table and moved the processing to
  symbolic_constant_parse.

 src/datatype.c |   30 +++++++++---------------------
 1 file changed, 9 insertions(+), 21 deletions(-)

diff --git a/src/datatype.c b/src/datatype.c
index fdfee54..9bfe46d 100644
--- a/src/datatype.c
+++ b/src/datatype.c
@@ -112,6 +112,7 @@ struct error_record *symbolic_constant_parse(const struct expr *sym,
 {
 	const struct symbolic_constant *s;
 	const struct datatype *dtype;
+	struct error_record *erec;
 
 	for (s = tbl->symbols; s->identifier != NULL; s++) {
 		if (!strcmp(sym->identifier, s->identifier))
@@ -119,8 +120,14 @@ struct error_record *symbolic_constant_parse(const struct expr *sym,
 	}
 
 	dtype = sym->dtype;
-	if (s->identifier == NULL)
-		return error(&sym->location, "Could not parse %s", dtype->desc);
+	if (s->identifier == NULL) {
+		erec = sym->dtype->basetype->parse(sym, res);
+		if (erec != NULL)
+			return erec;
+		if (*res)
+			return error(&sym->location, "Could not parse %s",
+				     dtype->desc);
+	}
 
 	*res = constant_expr_alloc(&sym->location, dtype,
 				   dtype->byteorder, dtype->size,
@@ -660,25 +667,6 @@ static void mark_type_print(const struct expr *expr)
 static struct error_record *mark_type_parse(const struct expr *sym,
 					    struct expr **res)
 {
-	struct error_record *erec;
-	const struct symbolic_constant *s;
-
-	for (s = mark_tbl->symbols; s->identifier != NULL; s++) {
-		if (!strcmp(sym->identifier, s->identifier)) {
-			*res = constant_expr_alloc(&sym->location, sym->dtype,
-						   sym->dtype->byteorder,
-						   sym->dtype->size,
-						   &s->value);
-			return NULL;
-		}
-	}
-
-	*res = NULL;
-	erec = sym->dtype->basetype->parse(sym, res);
-	if (erec != NULL)
-		return erec;
-	if (*res)
-		return NULL;
 	return symbolic_constant_parse(sym, mark_tbl, res);
 }
 
-- 
1.7.10.4


^ permalink raw reply related	[flat|nested] 7+ messages in thread

* [nft PATCH 4/4 v4] nft: complete reject support
  2014-09-26 16:50 [nft PATCH 1/4 v2] payload: generate dependency in the appropriate byteorder Alvaro Neira Ayuso
  2014-09-26 16:50 ` [nft PATCH 2/4 v2] src: Enhance payload_gen_dependency() Alvaro Neira Ayuso
  2014-09-26 16:50 ` [nft PATCH 3/4 v2] datatype: Enhance symbolic_constant_parse() Alvaro Neira Ayuso
@ 2014-09-26 16:50 ` Alvaro Neira Ayuso
  2014-09-26 16:56   ` Álvaro Neira Ayuso
  2014-09-26 18:06   ` Pablo Neira Ayuso
  2 siblings, 2 replies; 7+ messages in thread
From: Alvaro Neira Ayuso @ 2014-09-26 16:50 UTC (permalink / raw)
  To: netfilter-devel; +Cc: kaber

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-unreach

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-unreach

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-unreach

We have four icmpx reason and the mapping is:

ICMPX REASON	 	|	ICMPv6		|	ICMPv4
			|			|
admin-prohibited 	| admin-prohibited	|	admin-prohibited
port-unreach	 	| port-unreach		|	port-unreach
no-route	 	| no-route		|	net-unreach
host-unreach	 	| addr-unreach		|	host-unreach

Signed-off-by: Alvaro Neira Ayuso <alvaroneay@gmail.com>
---
[Changes in v4]
* Fixed some cosmetic errors
* Added the new datatype icmpx.

[Tested with the rules]
[Error ways]
Reject not supported for arp
 * nft add rule arp filter input reject (reject not supported for arp)

Use icmp code field in a ipv6 table or icmpv6 code field in ipv4 table
 * nft add rule ip filter input reject with icmp type no-route
 * nft add rule ip6 filter input reject with icmpv6 type host-unreach

Use tcp reset with a udp rule
 * nft add rule ip filter input udp dport 9999 reject with tcp reset

[Hits ways]
IPV4
 * nft add rule ip filter input udp dport 9999 /
 					reject with icmp type host-unreach
 * nft add rule ip filter input tcp dport 9999 reject
 * nft add rule ip filter input reject
 * nft add rule ip filter input reject with tcp reset

IPV6
 * nft add rule ip6 filter input udp dport 9999 reject
 * nft add rule ip6 filter input tcp dport 9999 /
   					reject with icmpv6 type admin-prohibited
 * nft add rule ip6 filter input reject

INET
 * nft add rule inet filter input reject with icmp type host-unreach
 * nft add rule inet filter input reject with icmpv6 type no-route
 * nft add rule inet filter input udp dport 9999 counter /
    					reject with icmpv6 type no-route
 * nft add rule inet filter input reject with tcp reset
 * nft add rule inet filter input reject

BRIDGE
 * nft add rule bridge filter input reject with icmp type host-unreach
 * nft add rule bridge filter input reject with icmpv6 type no-route
 * nft add rule bridge filter input reject

 include/datatype.h                  |    9 ++
 include/linux/netfilter/nf_tables.h |   21 +++++
 include/statement.h                 |    3 +
 src/datatype.c                      |  105 +++++++++++++++++++++++
 src/evaluate.c                      |  160 ++++++++++++++++++++++++++++++++++-
 src/netlink_delinearize.c           |   41 +++++++++
 src/netlink_linearize.c             |    5 +-
 src/parser.y                        |   54 +++++++++++-
 src/payload.c                       |   31 +++++--
 src/scanner.l                       |    3 +
 src/statement.c                     |   49 +++++++++++
 11 files changed, 471 insertions(+), 10 deletions(-)

diff --git a/include/datatype.h b/include/datatype.h
index 5182263..15fea44 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 b72ccfe..f04d997 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/statement.h b/include/statement.h
index 7a57f7d..574835c 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 9bfe46d..8ab6913 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)
@@ -683,6 +689,105 @@ const struct datatype mark_type = {
 	.flags		= DTYPE_F_PREFIX,
 };
 
+static const struct symbol_table icmp_code_tbl = {
+	.symbols	= {
+		SYMBOL("net-unreach",		ICMP_NET_UNREACH),
+		SYMBOL("host-unreach",		ICMP_HOST_UNREACH),
+		SYMBOL("prot-unreach",		ICMP_PROT_UNREACH),
+		SYMBOL("port-unreach",		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 type",
+	.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-unreach",		ICMP6_DST_UNREACH_ADDR),
+		SYMBOL("port-unreach",		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 type",
+	.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-unreach",		NFT_REJECT_ICMPX_PORT_UNREACH),
+		SYMBOL("admin-prohibited",	NFT_REJECT_ICMPX_ADMIN_PROHIBITED),
+		SYMBOL("no-route",		NFT_REJECT_ICMPX_NO_ROUTE),
+		SYMBOL("host-unreach",		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 type",
+	.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 52ce548..41a23d1 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,168 @@ 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 stmt_reject_gen_dependency(struct stmt *stmt, struct expr *expr,
+				      struct eval_ctx *ctx)
 {
+	struct expr *payload;
+	struct stmt *nstmt;
+	const struct proto_desc *desc, *base;
+
+	if (stmt->reject.type == NFT_REJECT_TCP_RST) {
+		desc = ctx->pctx.protocol[PROTO_BASE_TRANSPORT_HDR].desc;
+		if (desc != NULL)
+			return 0;
+		/* Generate a TCP dependency */
+		payload = payload_expr_alloc(&stmt->location, &proto_tcp,
+					     TCPHDR_DPORT);
+		goto gen_dep;
+	}
+
+	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");
+	}
+
+gen_dep:
+	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(stmt, expr, ctx) < 0)
+				return -1;
+			break;
+		case NFT_REJECT_ICMPX_UNREACH:
+			return stmt_error(ctx, stmt,
+				   "abstracted ICMP unreachable not supported");
+		default:
+			break;
+		}
+		break;
+	case NFPROTO_INET:
+	case NFPROTO_BRIDGE:
+		if (stmt->reject.type == NFT_REJECT_ICMPX_UNREACH)
+			break;
+		if (stmt_reject_gen_dependency(stmt, expr, ctx) < 0)
+			return -1;
+		break;
+	}
+
 	stmt->flags |= STMT_F_TERMINAL;
 	return 0;
 }
 
+/* The reason to reject this hasn't been specified */
+static int stmt_evaluate_reject_no_reason(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;
+}
+
+/* The reason to reject this has been specified with icmp reason */
+static int stmt_evaluate_reject_icmp_reason(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;
+}
+
+/* The reason to reject this has been specified with tcp reset */
+static int stmt_evaluate_reject_tcp_reason(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) {
+		stmt_evaluate_reject_no_reason(ctx, stmt);
+	} else if (stmt->reject.expr != NULL) {
+		if (stmt_evaluate_reject_icmp_reason(ctx, stmt) < 0)
+			return -1;
+	} else {
+		if (stmt_evaluate_reject_tcp_reason(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 796b632..12cb9e0 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>
@@ -474,6 +477,9 @@ static void netlink_parse_reject(struct netlink_parse_ctx *ctx,
 	struct stmt *stmt;
 
 	stmt = reject_stmt_alloc(loc);
+	stmt->reject.type = nft_rule_expr_get_u32(expr, NFT_EXPR_REJECT_TYPE);
+	stmt->reject.icmp_code = nft_rule_expr_get_u8(expr,
+						      NFT_EXPR_REJECT_CODE);
 	list_add_tail(&stmt->list, &ctx->rule->stmts);
 }
 
@@ -899,6 +905,38 @@ 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:
+	case NFPROTO_IPV6:
+		stmt->reject.family = rctx.pctx.family;
+		break;
+	case NFPROTO_INET:
+	case NFPROTO_BRIDGE:
+		if (rctx.pbase == PROTO_BASE_INVALID)
+			return;
+
+		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);
+		if (protocol == __constant_htons(ETH_P_IP))
+			stmt->reject.family = NFPROTO_IPV4;
+		else if (protocol == __constant_htons(ETH_P_IPV6))
+			stmt->reject.family = NFPROTO_IPV6;
+		else
+			stmt->reject.family = protocol;
+		break;
+	default:
+		break;
+	}
+}
 static void rule_parse_postprocess(struct netlink_parse_ctx *ctx, struct rule *rule)
 {
 	struct rule_pp_ctx rctx;
@@ -926,6 +964,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 c46b6d4..29f8e9a 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 32d5455..83d45d3 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 <rule.h>
@@ -362,6 +364,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"
@@ -423,8 +428,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
@@ -1346,12 +1351,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 b7b74ed..ebf8079 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 772f658..bbf2797 100644
--- a/src/scanner.l
+++ b/src/scanner.l
@@ -308,6 +308,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 8e4b49e..9ff6749 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>
@@ -224,9 +228,54 @@ struct stmt *queue_stmt_alloc(const struct location *loc)
 	return stmt_alloc(loc, &queue_stmt_ops);
 }
 
+const char *reject_stmt_code_ip[] = {
+	[ICMP_NET_UNREACH]	= "net-unreach",
+	[ICMP_HOST_UNREACH]	= "host-unreach",
+	[ICMP_PROT_UNREACH]	= "prot-unreach",
+	[ICMP_NET_ANO]		= "net-prohibited",
+	[ICMP_HOST_ANO]		= "host-prohibited",
+	[ICMP_PKT_FILTERED]	= "admin-prohibited",
+};
+
+const char *reject_stmt_code_ip6[] = {
+	[ICMP6_DST_UNREACH_NOROUTE]	= "no-route",
+	[ICMP6_DST_UNREACH_ADMIN]	= "admin-prohibited",
+	[ICMP6_DST_UNREACH_ADDR]	= "addr-unreach",
+};
+
+const char *reject_stmt_code_icmpx[] = {
+	[NFT_REJECT_ICMPX_NO_ROUTE]		= "no-route",
+	[NFT_REJECT_ICMPX_HOST_UNREACH]		= "host-unreach",
+	[NFT_REJECT_ICMPX_ADMIN_PROHIBITED]	= "admin-prohibited",
+};
+
 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 %s",
+			reject_stmt_code_icmpx[stmt->reject.icmp_code]);
+		break;
+	case NFT_REJECT_ICMP_UNREACH:
+		if (stmt->reject.family == NFPROTO_IPV4) {
+			if (stmt->reject.icmp_code == ICMP_PORT_UNREACH)
+				break;
+			printf(" with icmp type %s",
+			       reject_stmt_code_ip[stmt->reject.icmp_code]);
+		} else if (stmt->reject.family == NFPROTO_IPV6) {
+			if (stmt->reject.icmp_code == ICMP6_DST_UNREACH_NOPORT)
+				break;
+
+			printf(" with icmpv6 type %s",
+			       reject_stmt_code_ip6[stmt->reject.icmp_code]);
+		}
+	}
 }
 
 static const struct stmt_ops reject_stmt_ops = {
-- 
1.7.10.4


^ permalink raw reply related	[flat|nested] 7+ messages in thread

* Re: [nft PATCH 4/4 v4] nft: complete reject support
  2014-09-26 16:50 ` [nft PATCH 4/4 v4] nft: complete reject support Alvaro Neira Ayuso
@ 2014-09-26 16:56   ` Álvaro Neira Ayuso
  2014-09-26 18:06   ` Pablo Neira Ayuso
  1 sibling, 0 replies; 7+ messages in thread
From: Álvaro Neira Ayuso @ 2014-09-26 16:56 UTC (permalink / raw)
  To: netfilter-devel; +Cc: Patrick McHardy

El 26/09/14 18:50, Alvaro Neira Ayuso escribió:
> 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-unreach
>
> 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-unreach
>
> 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-unreach
>
> We have four icmpx reason and the mapping is:
>
> ICMPX REASON	 	|	ICMPv6		|	ICMPv4
> 			|			|
> admin-prohibited 	| admin-prohibited	|	admin-prohibited
> port-unreach	 	| port-unreach		|	port-unreach
> no-route	 	| no-route		|	net-unreach
> host-unreach	 	| addr-unreach		|	host-unreach
>
> Signed-off-by: Alvaro Neira Ayuso <alvaroneay@gmail.com>
> ---
> [Changes in v4]
> * Fixed some cosmetic errors
> * Added the new datatype icmpx.
>
> [Tested with the rules]
> [Error ways]
> Reject not supported for arp
>   * nft add rule arp filter input reject (reject not supported for arp)
>
> Use icmp code field in a ipv6 table or icmpv6 code field in ipv4 table
>   * nft add rule ip filter input reject with icmp type no-route
>   * nft add rule ip6 filter input reject with icmpv6 type host-unreach
>
> Use tcp reset with a udp rule
>   * nft add rule ip filter input udp dport 9999 reject with tcp reset
>
> [Hits ways]
> IPV4
>   * nft add rule ip filter input udp dport 9999 /
>   					reject with icmp type host-unreach
>   * nft add rule ip filter input tcp dport 9999 reject
>   * nft add rule ip filter input reject
>   * nft add rule ip filter input reject with tcp reset
>
> IPV6
>   * nft add rule ip6 filter input udp dport 9999 reject
>   * nft add rule ip6 filter input tcp dport 9999 /
>     					reject with icmpv6 type admin-prohibited
>   * nft add rule ip6 filter input reject
>
> INET
>   * nft add rule inet filter input reject with icmp type host-unreach
>   * nft add rule inet filter input reject with icmpv6 type no-route
>   * nft add rule inet filter input udp dport 9999 counter /
>      					reject with icmpv6 type no-route
>   * nft add rule inet filter input reject with tcp reset
>   * nft add rule inet filter input reject
>
> BRIDGE
>   * nft add rule bridge filter input reject with icmp type host-unreach
>   * nft add rule bridge filter input reject with icmpv6 type no-route
>   * nft add rule bridge filter input reject

Sorry, I forgot to add a example with icmpx. For example:

* nft add rule inet filter input reject with icmpx type no-route

>
>   include/datatype.h                  |    9 ++
>   include/linux/netfilter/nf_tables.h |   21 +++++
>   include/statement.h                 |    3 +
>   src/datatype.c                      |  105 +++++++++++++++++++++++
>   src/evaluate.c                      |  160 ++++++++++++++++++++++++++++++++++-
>   src/netlink_delinearize.c           |   41 +++++++++
>   src/netlink_linearize.c             |    5 +-
>   src/parser.y                        |   54 +++++++++++-
>   src/payload.c                       |   31 +++++--
>   src/scanner.l                       |    3 +
>   src/statement.c                     |   49 +++++++++++
>   11 files changed, 471 insertions(+), 10 deletions(-)
>
> diff --git a/include/datatype.h b/include/datatype.h
> index 5182263..15fea44 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 b72ccfe..f04d997 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/statement.h b/include/statement.h
> index 7a57f7d..574835c 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 9bfe46d..8ab6913 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)
> @@ -683,6 +689,105 @@ const struct datatype mark_type = {
>   	.flags		= DTYPE_F_PREFIX,
>   };
>
> +static const struct symbol_table icmp_code_tbl = {
> +	.symbols	= {
> +		SYMBOL("net-unreach",		ICMP_NET_UNREACH),
> +		SYMBOL("host-unreach",		ICMP_HOST_UNREACH),
> +		SYMBOL("prot-unreach",		ICMP_PROT_UNREACH),
> +		SYMBOL("port-unreach",		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 type",
> +	.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-unreach",		ICMP6_DST_UNREACH_ADDR),
> +		SYMBOL("port-unreach",		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 type",
> +	.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-unreach",		NFT_REJECT_ICMPX_PORT_UNREACH),
> +		SYMBOL("admin-prohibited",	NFT_REJECT_ICMPX_ADMIN_PROHIBITED),
> +		SYMBOL("no-route",		NFT_REJECT_ICMPX_NO_ROUTE),
> +		SYMBOL("host-unreach",		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 type",
> +	.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 52ce548..41a23d1 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,168 @@ 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 stmt_reject_gen_dependency(struct stmt *stmt, struct expr *expr,
> +				      struct eval_ctx *ctx)
>   {
> +	struct expr *payload;
> +	struct stmt *nstmt;
> +	const struct proto_desc *desc, *base;
> +
> +	if (stmt->reject.type == NFT_REJECT_TCP_RST) {
> +		desc = ctx->pctx.protocol[PROTO_BASE_TRANSPORT_HDR].desc;
> +		if (desc != NULL)
> +			return 0;
> +		/* Generate a TCP dependency */
> +		payload = payload_expr_alloc(&stmt->location, &proto_tcp,
> +					     TCPHDR_DPORT);
> +		goto gen_dep;
> +	}
> +
> +	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");
> +	}
> +
> +gen_dep:
> +	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(stmt, expr, ctx) < 0)
> +				return -1;
> +			break;
> +		case NFT_REJECT_ICMPX_UNREACH:
> +			return stmt_error(ctx, stmt,
> +				   "abstracted ICMP unreachable not supported");
> +		default:
> +			break;
> +		}
> +		break;
> +	case NFPROTO_INET:
> +	case NFPROTO_BRIDGE:
> +		if (stmt->reject.type == NFT_REJECT_ICMPX_UNREACH)
> +			break;
> +		if (stmt_reject_gen_dependency(stmt, expr, ctx) < 0)
> +			return -1;
> +		break;
> +	}
> +
>   	stmt->flags |= STMT_F_TERMINAL;
>   	return 0;
>   }
>
> +/* The reason to reject this hasn't been specified */
> +static int stmt_evaluate_reject_no_reason(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;
> +}
> +
> +/* The reason to reject this has been specified with icmp reason */
> +static int stmt_evaluate_reject_icmp_reason(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;
> +}
> +
> +/* The reason to reject this has been specified with tcp reset */
> +static int stmt_evaluate_reject_tcp_reason(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) {
> +		stmt_evaluate_reject_no_reason(ctx, stmt);
> +	} else if (stmt->reject.expr != NULL) {
> +		if (stmt_evaluate_reject_icmp_reason(ctx, stmt) < 0)
> +			return -1;
> +	} else {
> +		if (stmt_evaluate_reject_tcp_reason(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 796b632..12cb9e0 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>
> @@ -474,6 +477,9 @@ static void netlink_parse_reject(struct netlink_parse_ctx *ctx,
>   	struct stmt *stmt;
>
>   	stmt = reject_stmt_alloc(loc);
> +	stmt->reject.type = nft_rule_expr_get_u32(expr, NFT_EXPR_REJECT_TYPE);
> +	stmt->reject.icmp_code = nft_rule_expr_get_u8(expr,
> +						      NFT_EXPR_REJECT_CODE);
>   	list_add_tail(&stmt->list, &ctx->rule->stmts);
>   }
>
> @@ -899,6 +905,38 @@ 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:
> +	case NFPROTO_IPV6:
> +		stmt->reject.family = rctx.pctx.family;
> +		break;
> +	case NFPROTO_INET:
> +	case NFPROTO_BRIDGE:
> +		if (rctx.pbase == PROTO_BASE_INVALID)
> +			return;
> +
> +		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);
> +		if (protocol == __constant_htons(ETH_P_IP))
> +			stmt->reject.family = NFPROTO_IPV4;
> +		else if (protocol == __constant_htons(ETH_P_IPV6))
> +			stmt->reject.family = NFPROTO_IPV6;
> +		else
> +			stmt->reject.family = protocol;
> +		break;
> +	default:
> +		break;
> +	}
> +}
>   static void rule_parse_postprocess(struct netlink_parse_ctx *ctx, struct rule *rule)
>   {
>   	struct rule_pp_ctx rctx;
> @@ -926,6 +964,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 c46b6d4..29f8e9a 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 32d5455..83d45d3 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 <rule.h>
> @@ -362,6 +364,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"
> @@ -423,8 +428,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
> @@ -1346,12 +1351,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 b7b74ed..ebf8079 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 772f658..bbf2797 100644
> --- a/src/scanner.l
> +++ b/src/scanner.l
> @@ -308,6 +308,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 8e4b49e..9ff6749 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>
> @@ -224,9 +228,54 @@ struct stmt *queue_stmt_alloc(const struct location *loc)
>   	return stmt_alloc(loc, &queue_stmt_ops);
>   }
>
> +const char *reject_stmt_code_ip[] = {
> +	[ICMP_NET_UNREACH]	= "net-unreach",
> +	[ICMP_HOST_UNREACH]	= "host-unreach",
> +	[ICMP_PROT_UNREACH]	= "prot-unreach",
> +	[ICMP_NET_ANO]		= "net-prohibited",
> +	[ICMP_HOST_ANO]		= "host-prohibited",
> +	[ICMP_PKT_FILTERED]	= "admin-prohibited",
> +};
> +
> +const char *reject_stmt_code_ip6[] = {
> +	[ICMP6_DST_UNREACH_NOROUTE]	= "no-route",
> +	[ICMP6_DST_UNREACH_ADMIN]	= "admin-prohibited",
> +	[ICMP6_DST_UNREACH_ADDR]	= "addr-unreach",
> +};
> +
> +const char *reject_stmt_code_icmpx[] = {
> +	[NFT_REJECT_ICMPX_NO_ROUTE]		= "no-route",
> +	[NFT_REJECT_ICMPX_HOST_UNREACH]		= "host-unreach",
> +	[NFT_REJECT_ICMPX_ADMIN_PROHIBITED]	= "admin-prohibited",
> +};
> +
>   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 %s",
> +			reject_stmt_code_icmpx[stmt->reject.icmp_code]);
> +		break;
> +	case NFT_REJECT_ICMP_UNREACH:
> +		if (stmt->reject.family == NFPROTO_IPV4) {
> +			if (stmt->reject.icmp_code == ICMP_PORT_UNREACH)
> +				break;
> +			printf(" with icmp type %s",
> +			       reject_stmt_code_ip[stmt->reject.icmp_code]);
> +		} else if (stmt->reject.family == NFPROTO_IPV6) {
> +			if (stmt->reject.icmp_code == ICMP6_DST_UNREACH_NOPORT)
> +				break;
> +
> +			printf(" with icmpv6 type %s",
> +			       reject_stmt_code_ip6[stmt->reject.icmp_code]);
> +		}
> +	}
>   }
>
>   static const struct stmt_ops reject_stmt_ops = {
>

--
To unsubscribe from this list: send the line "unsubscribe netfilter-devel" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [nft PATCH 4/4 v4] nft: complete reject support
  2014-09-26 16:50 ` [nft PATCH 4/4 v4] nft: complete reject support Alvaro Neira Ayuso
  2014-09-26 16:56   ` Álvaro Neira Ayuso
@ 2014-09-26 18:06   ` Pablo Neira Ayuso
  2014-09-26 18:18     ` Patrick McHardy
  1 sibling, 1 reply; 7+ messages in thread
From: Pablo Neira Ayuso @ 2014-09-26 18:06 UTC (permalink / raw)
  To: Alvaro Neira Ayuso; +Cc: netfilter-devel, kaber

First off, thanks for the effort. Several comments below.

On Fri, Sep 26, 2014 at 06:50:55PM +0200, Alvaro Neira Ayuso wrote:
> +/* The reason to reject this hasn't been specified */

You can remove this comment above. You selected a function to
encapsulate the code per reason that explains the thing by itself.

> +static int stmt_evaluate_reject_no_reason(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;
> +}
> +
> +/* The reason to reject this has been specified with icmp reason */

Same thing here.

> +static int stmt_evaluate_reject_icmp_reason(struct eval_ctx *ctx,
> +					    struct stmt *stmt)

I'd suggest: stmt_evaluate_reject_with_icmp(...)

> +{
> +	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;
> +}
> +
> +/* The reason to reject this has been specified with tcp reset */

And here.

> +static int stmt_evaluate_reject_tcp_reason(struct eval_ctx *ctx,
> +					   struct stmt *stmt)

I'd suggest: stmt_evaluate_reject_with_tcp(...)

> +{
> +	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) {
> +		stmt_evaluate_reject_no_reason(ctx, stmt);
> +	} else if (stmt->reject.expr != NULL) {
> +		if (stmt_evaluate_reject_icmp_reason(ctx, stmt) < 0)
> +			return -1;
> +	} else {
> +		if (stmt_evaluate_reject_tcp_reason(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 796b632..12cb9e0 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>
> @@ -474,6 +477,9 @@ static void netlink_parse_reject(struct netlink_parse_ctx *ctx,
>  	struct stmt *stmt;
>  
>  	stmt = reject_stmt_alloc(loc);
> +	stmt->reject.type = nft_rule_expr_get_u32(expr, NFT_EXPR_REJECT_TYPE);
> +	stmt->reject.icmp_code = nft_rule_expr_get_u8(expr,
> +						      NFT_EXPR_REJECT_CODE);
>  	list_add_tail(&stmt->list, &ctx->rule->stmts);
>  }
>  
> @@ -899,6 +905,38 @@ 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:
> +	case NFPROTO_IPV6:
> +		stmt->reject.family = rctx.pctx.family;
> +		break;
> +	case NFPROTO_INET:
> +	case NFPROTO_BRIDGE:
> +		if (rctx.pbase == PROTO_BASE_INVALID)
> +			return;
> +
> +		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);
> +		if (protocol == __constant_htons(ETH_P_IP))
> +			stmt->reject.family = NFPROTO_IPV4;
> +		else if (protocol == __constant_htons(ETH_P_IPV6))
> +			stmt->reject.family = NFPROTO_IPV6;
> +		else
> +			stmt->reject.family = protocol;

I think you can generate an expression here to set up
stmt->reject.expr. That should help to simplify reject_stmt_print()
since you can use the new datatypes that you have defined.

I mean, with stmt->reject.expr, you can skip this:

+const char *reject_stmt_code_ip[] = {
+       [ICMP_NET_UNREACH]      = "net-unreach",
...

and get reject_stmt_print() in a diet.

I'm refering to the code in src/statement.c

[...]
> +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);

To fulfill the 80-chars per line rule, I'd suggest:

				$<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);

Same here.

> +				$<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);

And here.

^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [nft PATCH 4/4 v4] nft: complete reject support
  2014-09-26 18:06   ` Pablo Neira Ayuso
@ 2014-09-26 18:18     ` Patrick McHardy
  0 siblings, 0 replies; 7+ messages in thread
From: Patrick McHardy @ 2014-09-26 18:18 UTC (permalink / raw)
  To: Pablo Neira Ayuso, Alvaro Neira Ayuso; +Cc: netfilter-devel

Am 26. September 2014 20:06:12 MESZ, schrieb Pablo Neira Ayuso <pablo@netfilter.org>:
>First off, thanks for the effort. Several comments below.
>
>On Fri, Sep 26, 2014 at 06:50:55PM +0200, Alvaro Neira Ayuso wrote:
>> +/* The reason to reject this hasn't been specified */
>
>You can remove this comment above. You selected a function to
>encapsulate the code per reason that explains the thing by itself.
>
>> +static int stmt_evaluate_reject_no_reason(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;
>> +}
>> +
>> +/* The reason to reject this has been specified with icmp reason */
>
>Same thing here.
>
>> +static int stmt_evaluate_reject_icmp_reason(struct eval_ctx *ctx,
>> +					    struct stmt *stmt)
>
>I'd suggest: stmt_evaluate_reject_with_icmp(...)
>
>> +{
>> +	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;
>> +}
>> +
>> +/* The reason to reject this has been specified with tcp reset */
>
>And here.
>
>> +static int stmt_evaluate_reject_tcp_reason(struct eval_ctx *ctx,
>> +					   struct stmt *stmt)
>
>I'd suggest: stmt_evaluate_reject_with_tcp(...)

I'd like to propose to rename active reject to reset. Its more precise and not specific to a protocol.


^ permalink raw reply	[flat|nested] 7+ messages in thread

end of thread, other threads:[~2014-09-26 18:19 UTC | newest]

Thread overview: 7+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2014-09-26 16:50 [nft PATCH 1/4 v2] payload: generate dependency in the appropriate byteorder Alvaro Neira Ayuso
2014-09-26 16:50 ` [nft PATCH 2/4 v2] src: Enhance payload_gen_dependency() Alvaro Neira Ayuso
2014-09-26 16:50 ` [nft PATCH 3/4 v2] datatype: Enhance symbolic_constant_parse() Alvaro Neira Ayuso
2014-09-26 16:50 ` [nft PATCH 4/4 v4] nft: complete reject support Alvaro Neira Ayuso
2014-09-26 16:56   ` Álvaro Neira Ayuso
2014-09-26 18:06   ` Pablo Neira Ayuso
2014-09-26 18:18     ` Patrick McHardy

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).