[nft,1/2] Introduce socket matching

Message ID ec64cf9cb6bfce1693f9ff4073514fa9c3f6ab70.1526542099.git.ecklm94@gmail.com
State Changes Requested
Delegated to: Pablo Neira
Headers show
Series
  • Python test fixes
Related show

Commit Message

Máté Eckl May 17, 2018, 7:36 a.m.
Socket matching is achieved using the nft_compat interface.

The list of known limitations of the current implementation are:

* The absence of a corresponding socket cannot be matched (`socket
  missing`).
* Only transparent socket flag can be matched, nowildcard is not a flag,
  it should be matched with a different expression if desired. Other
  options that can be set with `setsockopt` are unavailable.
* Such a rule cannot be added to an `inet` table.

In the long term native implementation might be worth it.

Example:

table ip stable {
	chain tchain {
		type filter hook prerouting priority -150; policy accept;
		socket flags transparent counter packets 12 bytes 608 mark set 0x00000001 accept
		socket exists counter packets 52 bytes 3316
	}
}
table ip6 stable {
	chain tchain {
		type filter hook prerouting priority -150; policy accept;
		socket flags transparent counter packets 0 bytes 0 mark set 0x00000001 accept
		socket exists counter packets 0 bytes 0
	}
}

Signed-off-by: Máté Eckl <ecklm94@gmail.com>
---
 include/linux/netfilter/nf_tables.h |  4 ++++
 include/statement.h                 | 10 +++++++++
 include/xt.h                        |  4 ++--
 src/evaluate.c                      | 11 ++++++++++
 src/netlink_delinearize.c           | 19 ++++++++++++++++
 src/netlink_linearize.c             | 21 ++++++++++++++++++
 src/parser_bison.y                  | 31 ++++++++++++++++++++++++--
 src/scanner.l                       |  3 +++
 src/statement.c                     | 34 +++++++++++++++++++++++++++++
 src/xt.c                            |  2 +-
 10 files changed, 134 insertions(+), 5 deletions(-)

Comments

Máté Eckl May 17, 2018, 7:38 a.m. | #1
Originally I also added the following lines but it made the print too slow for
the test to pass.

It printed the following warning:
	inet/socket.t: WARNING: line 8: 'add rule ip sockip4 sockchain socket exists': 'socket exists' mismatches 'socke'
	inet/socket.t: WARNING: line 9: 'add rule ip sockip4 sockchain socket flags transparent': 'socket flags transparent' mismatches 'socket'

To be honest I don't know what this criterion means so if it is important please
notify me.

diff --git a/src/statement.c b/src/statement.c
index ff6a98a..ec3b0c0 100644
--- a/src/statement.c
+++ b/src/statement.c
@@ -183,6 +183,10 @@ static void socket_stmt_print(const struct stmt *stmt, struct output_ctx *octx)
 	     *existance_str = (s->exists) ? "exists" : "missing";
 
 	nft_print(octx, "socket");
+
+	if(octx->stateless)
+		return;
+
 	if (s->flags) {
 		__u8 f = s->flags;
 
--
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
Florian Westphal May 17, 2018, 1:21 p.m. | #2
Máté Eckl <ecklm94@gmail.com> wrote:
> Originally I also added the following lines but it made the print too slow for
> the test to pass.
> 
> It printed the following warning:
> 	inet/socket.t: WARNING: line 8: 'add rule ip sockip4 sockchain socket exists': 'socket exists' mismatches 'socke'
> 	inet/socket.t: WARNING: line 9: 'add rule ip sockip4 sockchain socket flags transparent': 'socket flags transparent' mismatches 'socket'
> 
> To be honest I don't know what this criterion means so if it is important please
> notify me.
> 
> diff --git a/src/statement.c b/src/statement.c
> index ff6a98a..ec3b0c0 100644
> --- a/src/statement.c
> +++ b/src/statement.c
> @@ -183,6 +183,10 @@ static void socket_stmt_print(const struct stmt *stmt, struct output_ctx *octx)
>  	     *existance_str = (s->exists) ? "exists" : "missing";
>  
>  	nft_print(octx, "socket");
> +
> +	if(octx->stateless)
> +		return;

This test makes no sense, socket match has no state.

This is for 'nft -s' which e.g. prints 'counter' instead
of 'counter x packets y bytes'.
--
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
Florian Westphal May 17, 2018, 2:42 p.m. | #3
Máté Eckl <ecklm94@gmail.com> wrote:
> +socket_stmt		:	SOCKET	EXISTS /* with the actual implementation we cannot match abscence */

I think we should go for a native expression.

I'll leave it up to you what you'd like to do next.
There are a few options:
1. First go for TPROXY in nft (i.e. finish userspace syntax first)
2. add socket expression for nf_tables.
3. add support for SYNPROXY (outside of your original proposal,
   but this can be done via nft_compat without loss of functionality).

I think 1 or 2 would make most sense, let me know.

In case you go for #2, i would go for net/netfilter/nft_socket.c,
you'll need a way to serialize the socket_stmt data via new netlink
attributes.

You can look at 2fa841938c648fe4359691f41e8e1f37ff1a3aa2 for
a commit that added a new expression.
--
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
Máté Eckl May 18, 2018, 7:24 a.m. | #4
On Thu, May 17, 2018 at 04:42:15PM +0200, Florian Westphal wrote:
> Máté Eckl <ecklm94@gmail.com> wrote:
> > +socket_stmt		:	SOCKET	EXISTS /* with the actual implementation we cannot match abscence */
> 
> I think we should go for a native expression.
> 
> I'll leave it up to you what you'd like to do next.
> There are a few options:
> 1. First go for TPROXY in nft (i.e. finish userspace syntax first)
> 2. add socket expression for nf_tables.
> 3. add support for SYNPROXY (outside of your original proposal,
>    but this can be done via nft_compat without loss of functionality).

I think I'll go for option 2.
--
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

Patch

diff --git a/include/linux/netfilter/nf_tables.h b/include/linux/netfilter/nf_tables.h
index 3395faf..31fd6f4 100644
--- a/include/linux/netfilter/nf_tables.h
+++ b/include/linux/netfilter/nf_tables.h
@@ -1284,6 +1284,10 @@  enum nft_fib_flags {
 	NFTA_FIB_F_PRESENT	= 1 << 5,	/* check existence only */
 };
 
+enum nft_socket_flags {
+	NFTA_SOCKET_TRANSPARENT = (1<<0),
+};
+
 enum nft_ct_helper_attributes {
 	NFTA_CT_HELPER_UNSPEC,
 	NFTA_CT_HELPER_NAME,
diff --git a/include/statement.h b/include/statement.h
index de26549..84a8f3f 100644
--- a/include/statement.h
+++ b/include/statement.h
@@ -32,6 +32,13 @@  struct counter_stmt {
 
 extern struct stmt *counter_stmt_alloc(const struct location *loc);
 
+struct socket_stmt {
+	bool exists;
+	__u8 flags;
+};
+
+extern struct stmt *socket_stmt_alloc(const struct location *loc, bool exists, __u8 flags);
+
 struct exthdr_stmt {
 	struct expr			*expr;
 	struct expr			*val;
@@ -248,6 +255,7 @@  extern struct stmt *xt_stmt_alloc(const struct location *loc);
  * @STMT_EXTHDR:	extension header statement
  * @STMT_FLOW_OFFLOAD:	flow offload statement
  * @STMT_MAP:		map statement
+ * @STMT_SOCKET:	socket statement
  */
 enum stmt_types {
 	STMT_INVALID,
@@ -273,6 +281,7 @@  enum stmt_types {
 	STMT_EXTHDR,
 	STMT_FLOW_OFFLOAD,
 	STMT_MAP,
+	STMT_SOCKET,
 };
 
 /**
@@ -335,6 +344,7 @@  struct stmt {
 		struct objref_stmt	objref;
 		struct flow_stmt	flow;
 		struct map_stmt		map;
+		struct socket_stmt	socket;
 	};
 };
 
diff --git a/include/xt.h b/include/xt.h
index 753511e..5b29522 100644
--- a/include/xt.h
+++ b/include/xt.h
@@ -14,7 +14,7 @@  void xt_stmt_release(const struct stmt *stmt);
 void netlink_parse_target(struct netlink_parse_ctx *ctx,
 			  const struct location *loc,
 			  const struct nftnl_expr *nle);
-void netlink_parse_match(struct netlink_parse_ctx *ctx,
+void xt_netlink_parse_match(struct netlink_parse_ctx *ctx,
 			 const struct location *loc,
 			 const struct nftnl_expr *nle);
 void stmt_xt_postprocess(struct rule_pp_ctx *rctx, struct stmt *stmt,
@@ -28,7 +28,7 @@  static inline void xt_stmt_release(const struct stmt *stmt) {}
 static inline void netlink_parse_target(struct netlink_parse_ctx *ctx,
 					const struct location *loc,
 					const struct nftnl_expr *nle) {}
-static inline void netlink_parse_match(struct netlink_parse_ctx *ctx,
+static inline void xt_netlink_parse_match(struct netlink_parse_ctx *ctx,
 				       const struct location *loc,
 				       const struct nftnl_expr *nle) {}
 static inline void stmt_xt_postprocess(struct rule_pp_ctx *rctx,
diff --git a/src/evaluate.c b/src/evaluate.c
index 4eb36e2..5222f4e 100644
--- a/src/evaluate.c
+++ b/src/evaluate.c
@@ -2686,6 +2686,15 @@  static int stmt_evaluate_objref(struct eval_ctx *ctx, struct stmt *stmt)
 	return 0;
 }
 
+static int stmt_evaluate_socket(struct eval_ctx *ctx, struct stmt *stmt)
+{
+	const struct socket_stmt * const s = &stmt->socket;
+
+	if (!s->exists && s->flags)
+		return -1;
+	return 0;
+}
+
 int stmt_evaluate(struct eval_ctx *ctx, struct stmt *stmt)
 {
 	if (ctx->debug_mask & NFT_DEBUG_EVALUATION) {
@@ -2737,6 +2746,8 @@  int stmt_evaluate(struct eval_ctx *ctx, struct stmt *stmt)
 		return stmt_evaluate_objref(ctx, stmt);
 	case STMT_MAP:
 		return stmt_evaluate_map(ctx, stmt);
+	case STMT_SOCKET:
+		return stmt_evaluate_socket(ctx, stmt);
 	default:
 		BUG("unknown statement type %s\n", stmt->ops->name);
 	}
diff --git a/src/netlink_delinearize.c b/src/netlink_delinearize.c
index 8f4035a..19c753a 100644
--- a/src/netlink_delinearize.c
+++ b/src/netlink_delinearize.c
@@ -27,6 +27,7 @@ 
 #include <sys/socket.h>
 #include <libnftnl/udata.h>
 #include <xt.h>
+#include <linux/netfilter/xt_socket.h>
 
 static int netlink_parse_expr(const struct nftnl_expr *nle,
 			      struct netlink_parse_ctx *ctx);
@@ -1278,6 +1279,24 @@  static void netlink_parse_objref(struct netlink_parse_ctx *ctx,
 	ctx->stmt = stmt;
 }
 
+static void netlink_parse_match(struct netlink_parse_ctx *ctx,
+				 const struct location *loc,
+				 const struct nftnl_expr *nle)
+{
+	if (!strcmp(nftnl_expr_get_str(nle, NFTNL_EXPR_MT_NAME), "socket") &&
+	    nftnl_expr_get_u32(nle, NFTNL_EXPR_MT_REV) == 3) {
+		const struct xt_socket_mtinfo3 *info;
+		uint32_t len = 0;
+
+		info = nftnl_expr_get(nle, NFTNL_EXPR_MT_INFO, &len);
+		if(!info)
+			return;
+		ctx->stmt = socket_stmt_alloc(loc, true, info->flags); // true is placeholder
+	} else {
+		xt_netlink_parse_match(ctx, loc, nle);
+	}
+}
+
 static const struct {
 	const char	*name;
 	void		(*parse)(struct netlink_parse_ctx *ctx,
diff --git a/src/netlink_linearize.c b/src/netlink_linearize.c
index 2ab8acc..5e9345a 100644
--- a/src/netlink_linearize.c
+++ b/src/netlink_linearize.c
@@ -19,6 +19,7 @@ 
 #include <gmputil.h>
 #include <utils.h>
 #include <netinet/in.h>
+#include <linux/netfilter/xt_socket.h>
 
 #include <linux/netfilter.h>
 #include <libnftnl/udata.h>
@@ -1155,6 +1156,24 @@  static void netlink_gen_flow_offload_stmt(struct netlink_linearize_ctx *ctx,
 	nftnl_rule_add_expr(ctx->nlr, nle);
 }
 
+static void netlink_gen_socket_match_stmt(struct netlink_linearize_ctx *ctx,
+					  const struct stmt *stmt)
+{
+	struct nftnl_expr *nle = alloc_nft_expr("match");
+	struct xt_socket_mtinfo3 *info;
+
+	nftnl_expr_set_str(nle, NFTNL_EXPR_MT_NAME, "socket");
+	nftnl_expr_set_u32(nle, NFTNL_EXPR_MT_REV, 3);
+
+	info = xzalloc(sizeof(struct xt_socket_mtinfo3));
+	info->flags = stmt->socket.flags;
+
+	nftnl_expr_set(nle, NFTNL_EXPR_MT_INFO, info, sizeof(struct xt_socket_mtinfo3));
+
+	nftnl_rule_add_expr(ctx->nlr, nle);
+}
+
+
 static void netlink_gen_set_stmt(struct netlink_linearize_ctx *ctx,
 				 const struct stmt *stmt)
 {
@@ -1283,6 +1302,8 @@  static void netlink_gen_stmt(struct netlink_linearize_ctx *ctx,
 		return netlink_gen_objref_stmt(ctx, stmt);
 	case STMT_MAP:
 		return netlink_gen_map_stmt(ctx, stmt);
+	case STMT_SOCKET:
+		return netlink_gen_socket_match_stmt(ctx, stmt);
 	default:
 		BUG("unknown statement type %s\n", stmt->ops->name);
 	}
diff --git a/src/parser_bison.y b/src/parser_bison.y
index 0e3ee84..67a5b6f 100644
--- a/src/parser_bison.y
+++ b/src/parser_bison.y
@@ -189,6 +189,9 @@  int nft_lex(void *, void *, void *);
 
 %token FIB			"fib"
 
+%token SOCKET			"socket"
+%token TRANSPARENT		"transparent"
+
 %token HOOK			"hook"
 %token DEVICE			"device"
 %token DEVICES			"devices"
@@ -547,8 +550,11 @@  int nft_lex(void *, void *, void *);
 
 %type <list>			stmt_list
 %destructor { stmt_list_free($$); xfree($$); } stmt_list
-%type <stmt>			stmt match_stmt verdict_stmt
-%destructor { stmt_free($$); }	stmt match_stmt verdict_stmt
+%type <stmt>			stmt match_stmt verdict_stmt	socket_stmt
+%destructor { stmt_free($$); }	stmt match_stmt verdict_stmt	socket_stmt
+
+%type <val>			socket_stmt_flag socket_stmt_flags
+
 %type <stmt>			counter_stmt counter_stmt_alloc
 %destructor { stmt_free($$); }	counter_stmt counter_stmt_alloc
 %type <stmt>			payload_stmt
@@ -2078,6 +2084,27 @@  stmt			:	verdict_stmt
 			|	fwd_stmt
 			|	set_stmt
 			|	map_stmt
+			|	socket_stmt
+			;
+
+socket_stmt_flag	:	TRANSPARENT { $$ = NFTA_SOCKET_TRANSPARENT; }
+			;
+
+socket_stmt_flags	:	socket_stmt_flags COMMA socket_stmt_flag
+			{
+				$$ = $1 | $3;
+			}
+			|	socket_stmt_flag
+			;
+
+socket_stmt		:	SOCKET	EXISTS /* with the actual implementation we cannot match abscence */
+			{
+				$$ = socket_stmt_alloc(&@$, true, 0);
+			}
+			|	SOCKET FLAGS socket_stmt_flags /* we suppose existance criterion in this case */
+			{
+				$$ = socket_stmt_alloc(&@$, true, $3);
+			}
 			;
 
 verdict_stmt		:	verdict_expr
diff --git a/src/scanner.l b/src/scanner.l
index 6a861cf..416bd27 100644
--- a/src/scanner.l
+++ b/src/scanner.l
@@ -258,6 +258,9 @@  addrstring	({macaddr}|{ip4addr}|{ip6addr})
 "ruleset"		{ return RULESET; }
 "trace"			{ return TRACE; }
 
+"socket"		{ return SOCKET; }
+"transparent"		{ return TRANSPARENT;}
+
 "accept"		{ return ACCEPT; }
 "drop"			{ return DROP; }
 "continue"		{ return CONTINUE; }
diff --git a/src/statement.c b/src/statement.c
index d291001..ff6a98a 100644
--- a/src/statement.c
+++ b/src/statement.c
@@ -176,6 +176,40 @@  struct stmt *counter_stmt_alloc(const struct location *loc)
 	return stmt;
 }
 
+static void socket_stmt_print(const struct stmt *stmt, struct output_ctx *octx)
+{
+	const struct socket_stmt *s = &stmt->socket;
+	const char *transp_str = "transparent",
+	     *existance_str = (s->exists) ? "exists" : "missing";
+
+	nft_print(octx, "socket");
+	if (s->flags) {
+		__u8 f = s->flags;
+
+		nft_print(octx, " flags ");
+		if(f & NFTA_SOCKET_TRANSPARENT)
+			nft_print(octx, "%s", transp_str);
+	} else {
+		nft_print(octx, " %s", existance_str);
+	}
+	// (!s->exists && s->flags) is impossible, see stmt_evaluate_socket
+}
+
+static const struct stmt_ops socket_stmt_ops = {
+	.type		= STMT_SOCKET,
+	.name		= "socket",
+	.print		= socket_stmt_print,
+};
+
+extern struct stmt *socket_stmt_alloc(const struct location *loc, bool exists, __u8 flags)
+{
+	struct stmt *stmt = stmt_alloc(loc, &socket_stmt_ops);
+
+	stmt->socket.exists = exists;
+	stmt->socket.flags = flags;
+	return stmt;
+}
+
 static const char *objref_type[NFT_OBJECT_MAX + 1] = {
 	[NFT_OBJECT_COUNTER]	= "counter",
 	[NFT_OBJECT_QUOTA]	= "quota",
diff --git a/src/xt.c b/src/xt.c
index 95d0c5f..4f7c235 100644
--- a/src/xt.c
+++ b/src/xt.c
@@ -188,7 +188,7 @@  static struct xtables_match *xt_match_clone(struct xtables_match *m)
  * Delinearization
  */
 
-void netlink_parse_match(struct netlink_parse_ctx *ctx,
+void xt_netlink_parse_match(struct netlink_parse_ctx *ctx,
 			 const struct location *loc,
 			 const struct nftnl_expr *nle)
 {