netfilter-devel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH nft 1/2] src: use expression to store the log prefix
@ 2020-07-08  9:27 Pablo Neira Ayuso
  2020-07-08  9:27 ` [PATCH nft 2/2] src: allow for variables in the log prefix string Pablo Neira Ayuso
  0 siblings, 1 reply; 2+ messages in thread
From: Pablo Neira Ayuso @ 2020-07-08  9:27 UTC (permalink / raw)
  To: netfilter-devel

Intsead of using an array of char.

Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
---
 include/expression.h             |  2 ++
 include/linux/netfilter/nf_log.h |  3 +++
 include/statement.h              |  2 +-
 src/expression.c                 |  9 +++++++++
 src/json.c                       |  9 ++++++---
 src/netlink_delinearize.c        |  6 +++++-
 src/netlink_linearize.c          |  7 +++++--
 src/parser_bison.y               |  7 ++++++-
 src/parser_json.c                |  4 +++-
 src/statement.c                  | 11 ++++++++---
 10 files changed, 48 insertions(+), 12 deletions(-)

diff --git a/include/expression.h b/include/expression.h
index 8135a516cf3a..87937a5040b3 100644
--- a/include/expression.h
+++ b/include/expression.h
@@ -381,6 +381,8 @@ extern const struct datatype *expr_basetype(const struct expr *expr);
 extern void expr_set_type(struct expr *expr, const struct datatype *dtype,
 			  enum byteorder byteorder);
 
+void expr_to_string(const struct expr *expr, char *string);
+
 struct eval_ctx;
 extern int expr_binary_error(struct list_head *msgs,
 			     const struct expr *e1, const struct expr *e2,
diff --git a/include/linux/netfilter/nf_log.h b/include/linux/netfilter/nf_log.h
index 8be21e02387d..2ae00932d3d2 100644
--- a/include/linux/netfilter/nf_log.h
+++ b/include/linux/netfilter/nf_log.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
 #ifndef _NETFILTER_NF_LOG_H
 #define _NETFILTER_NF_LOG_H
 
@@ -9,4 +10,6 @@
 #define NF_LOG_MACDECODE	0x20	/* Decode MAC header */
 #define NF_LOG_MASK		0x2f
 
+#define NF_LOG_PREFIXLEN	128
+
 #endif /* _NETFILTER_NF_LOG_H */
diff --git a/include/statement.h b/include/statement.h
index 7d96b3947dfc..061bc6194915 100644
--- a/include/statement.h
+++ b/include/statement.h
@@ -75,7 +75,7 @@ enum {
 };
 
 struct log_stmt {
-	const char		*prefix;
+	struct expr		*prefix;
 	unsigned int		snaplen;
 	uint16_t		group;
 	uint16_t		qthreshold;
diff --git a/src/expression.c b/src/expression.c
index a6bde70f508e..fe529f98de7b 100644
--- a/src/expression.c
+++ b/src/expression.c
@@ -175,6 +175,15 @@ void expr_describe(const struct expr *expr, struct output_ctx *octx)
 	}
 }
 
+void expr_to_string(const struct expr *expr, char *string)
+{
+	int len = expr->len / BITS_PER_BYTE;
+
+	assert(expr->dtype == &string_type);
+
+	mpz_export_data(string, expr->value, BYTEORDER_HOST_ENDIAN, len);
+}
+
 void expr_set_type(struct expr *expr, const struct datatype *dtype,
 		   enum byteorder byteorder)
 {
diff --git a/src/json.c b/src/json.c
index ed7131816d7d..24583060e68e 100644
--- a/src/json.c
+++ b/src/json.c
@@ -1224,9 +1224,12 @@ json_t *log_stmt_json(const struct stmt *stmt, struct output_ctx *octx)
 {
 	json_t *root = json_object(), *flags;
 
-	if (stmt->log.flags & STMT_LOG_PREFIX)
-		json_object_set_new(root, "prefix",
-				    json_string(stmt->log.prefix));
+	if (stmt->log.flags & STMT_LOG_PREFIX) {
+		char prefix[NF_LOG_PREFIXLEN] = {};
+
+		expr_to_string(stmt->log.prefix, prefix);
+		json_object_set_new(root, "prefix", json_string(prefix));
+	}
 	if (stmt->log.flags & STMT_LOG_GROUP)
 		json_object_set_new(root, "group",
 				    json_integer(stmt->log.group));
diff --git a/src/netlink_delinearize.c b/src/netlink_delinearize.c
index 8de4830c4f80..7d7e07cf89ce 100644
--- a/src/netlink_delinearize.c
+++ b/src/netlink_delinearize.c
@@ -901,7 +901,11 @@ static void netlink_parse_log(struct netlink_parse_ctx *ctx,
 	stmt = log_stmt_alloc(loc);
 	prefix = nftnl_expr_get_str(nle, NFTNL_EXPR_LOG_PREFIX);
 	if (nftnl_expr_is_set(nle, NFTNL_EXPR_LOG_PREFIX)) {
-		stmt->log.prefix = xstrdup(prefix);
+		stmt->log.prefix = constant_expr_alloc(&internal_location,
+						       &string_type,
+						       BYTEORDER_HOST_ENDIAN,
+						       (strlen(prefix) + 1) * BITS_PER_BYTE,
+						       prefix);
 		stmt->log.flags |= STMT_LOG_PREFIX;
 	}
 	if (nftnl_expr_is_set(nle, NFTNL_EXPR_LOG_GROUP)) {
diff --git a/src/netlink_linearize.c b/src/netlink_linearize.c
index 08f7f89f1066..528f1e5cd0fe 100644
--- a/src/netlink_linearize.c
+++ b/src/netlink_linearize.c
@@ -10,6 +10,7 @@
  */
 
 #include <linux/netfilter/nf_tables.h>
+#include <linux/netfilter/nf_log.h>
 
 #include <string.h>
 #include <rule.h>
@@ -1006,8 +1007,10 @@ static void netlink_gen_log_stmt(struct netlink_linearize_ctx *ctx,
 
 	nle = alloc_nft_expr("log");
 	if (stmt->log.prefix != NULL) {
-		nftnl_expr_set_str(nle, NFTNL_EXPR_LOG_PREFIX,
-				      stmt->log.prefix);
+		char prefix[NF_LOG_PREFIXLEN] = {};
+
+		expr_to_string(stmt->log.prefix, prefix);
+		nftnl_expr_set_str(nle, NFTNL_EXPR_LOG_PREFIX, prefix);
 	}
 	if (stmt->log.flags & STMT_LOG_GROUP) {
 		nftnl_expr_set_u16(nle, NFTNL_EXPR_LOG_GROUP, stmt->log.group);
diff --git a/src/parser_bison.y b/src/parser_bison.y
index 72e67186c913..2fecc3472fba 100644
--- a/src/parser_bison.y
+++ b/src/parser_bison.y
@@ -2636,7 +2636,12 @@ log_args		:	log_arg
 
 log_arg			:	PREFIX			string
 			{
-				$<stmt>0->log.prefix	 = $2;
+				struct expr *expr;
+
+				expr = constant_expr_alloc(&@$, &string_type,
+							   BYTEORDER_HOST_ENDIAN,
+							   strlen($2) * BITS_PER_BYTE, $2);
+				$<stmt>0->log.prefix	 = expr;
 				$<stmt>0->log.flags 	|= STMT_LOG_PREFIX;
 			}
 			|	GROUP			NUM
diff --git a/src/parser_json.c b/src/parser_json.c
index 9fdef6913ad5..59347168cdc8 100644
--- a/src/parser_json.c
+++ b/src/parser_json.c
@@ -2159,7 +2159,9 @@ static struct stmt *json_parse_log_stmt(struct json_ctx *ctx,
 	stmt = log_stmt_alloc(int_loc);
 
 	if (!json_unpack(value, "{s:s}", "prefix", &tmpstr)) {
-		stmt->log.prefix = xstrdup(tmpstr);
+		stmt->log.prefix = constant_expr_alloc(int_loc, &string_type,
+						       BYTEORDER_HOST_ENDIAN,
+						       (strlen(tmpstr) + 1) * BITS_PER_BYTE, tmpstr);
 		stmt->log.flags |= STMT_LOG_PREFIX;
 	}
 	if (!json_unpack(value, "{s:i}", "group", &tmp)) {
diff --git a/src/statement.c b/src/statement.c
index 21a1bc8d40dd..afedbba21b75 100644
--- a/src/statement.c
+++ b/src/statement.c
@@ -18,6 +18,7 @@
 
 #include <arpa/inet.h>
 #include <linux/netfilter.h>
+#include <linux/netfilter/nf_log.h>
 #include <netinet/ip_icmp.h>
 #include <netinet/icmp6.h>
 #include <statement.h>
@@ -300,8 +301,12 @@ int log_level_parse(const char *level)
 static void log_stmt_print(const struct stmt *stmt, struct output_ctx *octx)
 {
 	nft_print(octx, "log");
-	if (stmt->log.flags & STMT_LOG_PREFIX)
-		nft_print(octx, " prefix \"%s\"", stmt->log.prefix);
+	if (stmt->log.flags & STMT_LOG_PREFIX) {
+		char prefix[NF_LOG_PREFIXLEN] = {};
+
+		expr_to_string(stmt->log.prefix, prefix);
+		nft_print(octx, " prefix \"%s\"", prefix);
+	}
 	if (stmt->log.flags & STMT_LOG_GROUP)
 		nft_print(octx, " group %u", stmt->log.group);
 	if (stmt->log.flags & STMT_LOG_SNAPLEN)
@@ -338,7 +343,7 @@ static void log_stmt_print(const struct stmt *stmt, struct output_ctx *octx)
 
 static void log_stmt_destroy(struct stmt *stmt)
 {
-	xfree(stmt->log.prefix);
+	expr_free(stmt->log.prefix);
 }
 
 static const struct stmt_ops log_stmt_ops = {
-- 
2.20.1


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

* [PATCH nft 2/2] src: allow for variables in the log prefix string
  2020-07-08  9:27 [PATCH nft 1/2] src: use expression to store the log prefix Pablo Neira Ayuso
@ 2020-07-08  9:27 ` Pablo Neira Ayuso
  0 siblings, 0 replies; 2+ messages in thread
From: Pablo Neira Ayuso @ 2020-07-08  9:27 UTC (permalink / raw)
  To: netfilter-devel

[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #1: Type: text/plain; charset=y, Size: 7279 bytes --]

For example:

 define test = "state"
 define foo = "match"

 table x {
        chain y {
                ct state invalid log prefix "invalid $test $foo:"
        }
 }

This patch scans for variables in the log prefix string. The log prefix
expression is a list of constant and variable expression that are
converted into a constant expression from the evaluation phase.

Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
---
 src/evaluate.c                                |  49 ++++++-
 src/parser_bison.y                            | 122 +++++++++++++++++-
 .../optionals/dumps/log_prefix_0.nft          |   5 +
 tests/shell/testcases/optionals/log_prefix_0  |  16 +++
 4 files changed, 187 insertions(+), 5 deletions(-)
 create mode 100644 tests/shell/testcases/optionals/dumps/log_prefix_0.nft
 create mode 100755 tests/shell/testcases/optionals/log_prefix_0

diff --git a/src/evaluate.c b/src/evaluate.c
index 640a7d465bae..d3368bacc6af 100644
--- a/src/evaluate.c
+++ b/src/evaluate.c
@@ -19,6 +19,7 @@
 #include <linux/netfilter/nf_tables.h>
 #include <linux/netfilter/nf_synproxy.h>
 #include <linux/netfilter/nf_nat.h>
+#include <linux/netfilter/nf_log.h>
 #include <linux/netfilter_ipv4.h>
 #include <netinet/ip_icmp.h>
 #include <netinet/icmp6.h>
@@ -3203,8 +3204,50 @@ static int stmt_evaluate_queue(struct eval_ctx *ctx, struct stmt *stmt)
 	return 0;
 }
 
+static int stmt_evaluate_log_prefix(struct eval_ctx *ctx, struct stmt *stmt)
+{
+	char prefix[NF_LOG_PREFIXLEN] = {}, tmp[NF_LOG_PREFIXLEN] = {};
+	int len = sizeof(prefix), offset = 0, ret;
+	struct expr *expr;
+	size_t size = 0;
+
+	if (stmt->log.prefix->etype != EXPR_LIST)
+		return 0;
+
+	list_for_each_entry(expr, &stmt->log.prefix->expressions, list) {
+		switch (expr->etype) {
+		case EXPR_VALUE:
+			expr_to_string(expr, tmp);
+			ret = snprintf(prefix + offset, len, "%s", tmp);
+			break;
+		case EXPR_VARIABLE:
+			ret = snprintf(prefix + offset, len, "%s",
+				       expr->sym->expr->identifier);
+			break;
+		default:
+			BUG("unknown expresion type %s\n", expr_name(expr));
+			break;
+		}
+		SNPRINTF_BUFFER_SIZE(ret, size, len, offset);
+	}
+
+	if (len == NF_LOG_PREFIXLEN)
+		return stmt_error(ctx, stmt, "log prefix is too long");
+
+	expr_free(stmt->log.prefix);
+
+	stmt->log.prefix =
+		constant_expr_alloc(&stmt->log.prefix->location, &string_type,
+				    BYTEORDER_HOST_ENDIAN,
+				    strlen(prefix) * BITS_PER_BYTE,
+				    prefix);
+	return 0;
+}
+
 static int stmt_evaluate_log(struct eval_ctx *ctx, struct stmt *stmt)
 {
+	int ret = 0;
+
 	if (stmt->log.flags & (STMT_LOG_GROUP | STMT_LOG_SNAPLEN |
 			       STMT_LOG_QTHRESHOLD)) {
 		if (stmt->log.flags & STMT_LOG_LEVEL)
@@ -3218,7 +3261,11 @@ static int stmt_evaluate_log(struct eval_ctx *ctx, struct stmt *stmt)
 	    (stmt->log.flags & ~STMT_LOG_LEVEL || stmt->log.logflags))
 		return stmt_error(ctx, stmt,
 				  "log level audit doesn't support any further options");
-	return 0;
+
+	if (stmt->log.prefix)
+		ret = stmt_evaluate_log_prefix(ctx, stmt);
+
+	return ret;
 }
 
 static int stmt_evaluate_set(struct eval_ctx *ctx, struct stmt *stmt)
diff --git a/src/parser_bison.y b/src/parser_bison.y
index 2fecc3472fba..face99507b82 100644
--- a/src/parser_bison.y
+++ b/src/parser_bison.y
@@ -2636,11 +2636,125 @@ log_args		:	log_arg
 
 log_arg			:	PREFIX			string
 			{
-				struct expr *expr;
+				struct scope *scope = current_scope(state);
+				bool done = false, another_var = false;
+				char *start, *end, scratch = '\0';
+				struct expr *expr, *item;
+				struct symbol *sym;
+				enum {
+					PARSE_TEXT,
+					PARSE_VAR,
+				} prefix_state;
+
+				/* No variables in log prefix, skip. */
+				if (!strchr($2, '$')) {
+					expr = constant_expr_alloc(&@$, &string_type,
+								   BYTEORDER_HOST_ENDIAN,
+								   (strlen($2) + 1) * BITS_PER_BYTE, $2);
+					$<stmt>0->log.prefix = expr;
+					$<stmt>0->log.flags |= STMT_LOG_PREFIX;
+					break;
+				}
 
-				expr = constant_expr_alloc(&@$, &string_type,
-							   BYTEORDER_HOST_ENDIAN,
-							   strlen($2) * BITS_PER_BYTE, $2);
+				/* Parse variables in log prefix string using a
+				 * state machine parser with two states. This
+				 * parser creates list of expressions composed
+				 * of constant and variable expressions.
+				 */
+				expr = compound_expr_alloc(&@$, EXPR_LIST);
+
+				start = (char *)$2;
+
+				if (*start != '$') {
+					prefix_state = PARSE_TEXT;
+				} else {
+					prefix_state = PARSE_VAR;
+					start++;
+				}
+				end = start;
+
+				/* Not nice, but works. */
+				while (!done) {
+					switch (prefix_state) {
+					case PARSE_TEXT:
+						while (*end != '\0' && *end != '$')
+							end++;
+
+						if (*end == '\0')
+							done = true;
+
+						*end = '\0';
+						item = constant_expr_alloc(&@$, &string_type,
+									   BYTEORDER_HOST_ENDIAN,
+									   (strlen(start) + 1) * BITS_PER_BYTE,
+									   start);
+						compound_expr_add(expr, item);
+
+						if (done)
+							break;
+
+						start = end + 1;
+						end = start;
+
+						/* fall through */
+					case PARSE_VAR:
+						while (isalnum(*end) || *end == '_')
+							end++;
+
+						if (*end == '\0')
+							done = true;
+						else if (*end == '$')
+							another_var = true;
+						else
+							scratch = *end;
+
+						*end = '\0';
+
+						sym = symbol_get(scope, start);
+						if (!sym) {
+							sym = symbol_lookup_fuzzy(scope, start);
+							if (sym) {
+								erec_queue(error(&@2, "unknown identifier '%s'; "
+										 "did you mean identifier ‘%s’?",
+										 start, sym->identifier),
+									   state->msgs);
+							} else {
+								erec_queue(error(&@2, "unknown identifier '%s'",
+										 start),
+									   state->msgs);
+							}
+							expr_free(expr);
+							xfree($2);
+							YYERROR;
+						}
+						item = variable_expr_alloc(&@$, scope, sym);
+						compound_expr_add(expr, item);
+
+						if (done)
+							break;
+
+						/* Restore original byte after
+						 * symbol lookup.
+						 */
+						if (scratch) {
+							*end = scratch;
+							scratch = '\0';
+						}
+
+						start = end;
+						if (another_var) {
+							another_var = false;
+							start++;
+							prefix_state = PARSE_VAR;
+						} else {
+							prefix_state = PARSE_TEXT;
+						}
+						end = start;
+						break;
+					}
+				}
+
+				xfree($2);
 				$<stmt>0->log.prefix	 = expr;
 				$<stmt>0->log.flags 	|= STMT_LOG_PREFIX;
 			}
diff --git a/tests/shell/testcases/optionals/dumps/log_prefix_0.nft b/tests/shell/testcases/optionals/dumps/log_prefix_0.nft
new file mode 100644
index 000000000000..8c11d697f9f2
--- /dev/null
+++ b/tests/shell/testcases/optionals/dumps/log_prefix_0.nft
@@ -0,0 +1,5 @@
+table ip x {
+	chain y {
+		ct state invalid log prefix "invalid state match, logging:"
+	}
+}
diff --git a/tests/shell/testcases/optionals/log_prefix_0 b/tests/shell/testcases/optionals/log_prefix_0
new file mode 100755
index 000000000000..513a9e74b92e
--- /dev/null
+++ b/tests/shell/testcases/optionals/log_prefix_0
@@ -0,0 +1,16 @@
+#!/bin/bash
+
+set -e
+
+TMP=$(mktemp)
+
+RULESET='define test = "state"
+define foo = "match, logging"
+
+table x {
+        chain y {
+                ct state invalid log prefix "invalid $test $foo:"
+        }
+}'
+
+$NFT -f - <<< "$RULESET"
-- 
2.20.1


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

end of thread, other threads:[~2020-07-08  9:27 UTC | newest]

Thread overview: 2+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2020-07-08  9:27 [PATCH nft 1/2] src: use expression to store the log prefix Pablo Neira Ayuso
2020-07-08  9:27 ` [PATCH nft 2/2] src: allow for variables in the log prefix string Pablo Neira Ayuso

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).