[PATCH nft] src: add string preprocessor and use it for log prefix string

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Add a string preprocessor to identify and replace variables in a string.
Rework existing support to variables in log prefix strings to use it.

Fixes: e76bb3794018 ("src: allow for variables in the log prefix string")
Signed-off-by: Pablo Neira Ayuso <pablo@xxxxxxxxxxxxx>
---
 Makefile.am               |   1 +
 include/expression.h      |   2 -
 include/parser.h          |   4 +
 include/statement.h       |   2 +-
 src/evaluate.c            |  45 +---------
 src/expression.c          |   9 --
 src/json.c                |   7 +-
 src/netlink_delinearize.c |   6 +-
 src/netlink_linearize.c   |   7 +-
 src/optimize.c            |   6 +-
 src/parser_bison.y        | 126 ++--------------------------
 src/parser_json.c         |   4 +-
 src/preprocess.c          | 168 ++++++++++++++++++++++++++++++++++++++
 src/statement.c           |  10 +--
 14 files changed, 198 insertions(+), 199 deletions(-)
 create mode 100644 src/preprocess.c

diff --git a/Makefile.am b/Makefile.am
index fef1d8d16321..9088170bfc68 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -232,6 +232,7 @@ src_libnftables_la_SOURCES = \
 	src/osf.c \
 	src/owner.c \
 	src/payload.c \
+	src/preprocess.c \
 	src/print.c \
 	src/proto.c \
 	src/rt.c \
diff --git a/include/expression.h b/include/expression.h
index 01b45b7c83b9..8982110cce95 100644
--- a/include/expression.h
+++ b/include/expression.h
@@ -415,8 +415,6 @@ 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/parser.h b/include/parser.h
index f79a22f306af..576e5e43e688 100644
--- a/include/parser.h
+++ b/include/parser.h
@@ -112,4 +112,8 @@ extern void scanner_push_buffer(void *scanner,
 
 extern void scanner_pop_start_cond(void *scanner, enum startcond_type sc);
 
+const char *str_preprocess(struct parser_state *state, struct location *loc,
+			   struct scope *scope, const char *x,
+			   struct error_record **rec);
+
 #endif /* NFTABLES_PARSER_H */
diff --git a/include/statement.h b/include/statement.h
index 662f99ddef79..9376911bb377 100644
--- a/include/statement.h
+++ b/include/statement.h
@@ -90,7 +90,7 @@ enum {
 };
 
 struct log_stmt {
-	struct expr		*prefix;
+	const char		*prefix;
 	unsigned int		snaplen;
 	uint16_t		group;
 	uint16_t		qthreshold;
diff --git a/src/evaluate.c b/src/evaluate.c
index 227f5da86382..aa9293a87856 100644
--- a/src/evaluate.c
+++ b/src/evaluate.c
@@ -4440,49 +4440,12 @@ static int stmt_evaluate_queue(struct eval_ctx *ctx, struct stmt *stmt)
 
 static int stmt_evaluate_log_prefix(struct eval_ctx *ctx, struct stmt *stmt)
 {
-	char tmp[NF_LOG_PREFIXLEN] = {};
-	char prefix[NF_LOG_PREFIXLEN];
-	size_t len = sizeof(prefix);
-	size_t offset = 0;
-	struct expr *expr;
-
-	if (stmt->log.prefix->etype != EXPR_LIST) {
-		if (stmt->log.prefix &&
-		    div_round_up(stmt->log.prefix->len, BITS_PER_BYTE) >= NF_LOG_PREFIXLEN)
-			return expr_error(ctx->msgs, stmt->log.prefix, "log prefix is too long");
-
-		return 0;
-	}
-
-	prefix[0] = '\0';
-
-	list_for_each_entry(expr, &stmt->log.prefix->expressions, list) {
-		int ret;
-
-		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 expression type %s\n", expr_name(expr));
-			break;
-		}
-		SNPRINTF_BUFFER_SIZE(ret, &len, &offset);
-	}
+	unsigned int len = strlen(stmt->log.prefix);
 
-	if (len == 0)
+	if (len >= NF_LOG_PREFIXLEN)
 		return stmt_error(ctx, stmt, "log prefix is too long");
-
-	expr = constant_expr_alloc(&stmt->log.prefix->location, &string_type,
-				   BYTEORDER_HOST_ENDIAN,
-				   strlen(prefix) * BITS_PER_BYTE, prefix);
-	expr_free(stmt->log.prefix);
-	stmt->log.prefix = expr;
+	else if (len == 0)
+		return stmt_error(ctx, stmt, "log prefix must have a minimum length of 1 character");
 
 	return 0;
 }
diff --git a/src/expression.c b/src/expression.c
index cb2573fec457..992f51064051 100644
--- a/src/expression.c
+++ b/src/expression.c
@@ -183,15 +183,6 @@ 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 b4fad0abd4b3..b1531ff3f4c9 100644
--- a/src/json.c
+++ b/src/json.c
@@ -1343,12 +1343,9 @@ 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) {
-		char prefix[NF_LOG_PREFIXLEN] = {};
+	if (stmt->log.flags & STMT_LOG_PREFIX)
+		json_object_set_new(root, "prefix", json_string(stmt->log.prefix));
 
-		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 da9f7a91e4b3..82e68999a432 100644
--- a/src/netlink_delinearize.c
+++ b/src/netlink_delinearize.c
@@ -1090,11 +1090,7 @@ 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 = constant_expr_alloc(&internal_location,
-						       &string_type,
-						       BYTEORDER_HOST_ENDIAN,
-						       (strlen(prefix) + 1) * BITS_PER_BYTE,
-						       prefix);
+		stmt->log.prefix = xstrdup(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 6204d8fd2668..a8a717336e78 100644
--- a/src/netlink_linearize.c
+++ b/src/netlink_linearize.c
@@ -1146,12 +1146,9 @@ static void netlink_gen_log_stmt(struct netlink_linearize_ctx *ctx,
 	struct nftnl_expr *nle;
 
 	nle = alloc_nft_expr("log");
-	if (stmt->log.prefix != NULL) {
-		char prefix[NF_LOG_PREFIXLEN] = {};
+	if (stmt->log.prefix != NULL)
+		nftnl_expr_set_str(nle, NFTNL_EXPR_LOG_PREFIX, stmt->log.prefix);
 
-		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);
 		if (stmt->log.flags & STMT_LOG_SNAPLEN)
diff --git a/src/optimize.c b/src/optimize.c
index b90dd995b13e..1dd08586f326 100644
--- a/src/optimize.c
+++ b/src/optimize.c
@@ -215,9 +215,7 @@ static bool __stmt_type_eq(const struct stmt *stmt_a, const struct stmt *stmt_b,
 		if (!stmt_a->log.prefix)
 			return true;
 
-		if (stmt_a->log.prefix->etype != EXPR_VALUE ||
-		    stmt_b->log.prefix->etype != EXPR_VALUE ||
-		    mpz_cmp(stmt_a->log.prefix->value, stmt_b->log.prefix->value))
+		if (strcmp(stmt_a->log.prefix, stmt_b->log.prefix))
 			return false;
 		break;
 	case STMT_REJECT:
@@ -406,7 +404,7 @@ static int rule_collect_stmts(struct optimize_ctx *ctx, struct rule *rule)
 		case STMT_LOG:
 			memcpy(&clone->log, &stmt->log, sizeof(clone->log));
 			if (stmt->log.prefix)
-				clone->log.prefix = expr_get(stmt->log.prefix);
+				clone->log.prefix = xstrdup(stmt->log.prefix);
 			break;
 		case STMT_NAT:
 			if ((stmt->nat.addr &&
diff --git a/src/parser_bison.y b/src/parser_bison.y
index 53f45315ef46..f3f71801643d 100644
--- a/src/parser_bison.y
+++ b/src/parser_bison.y
@@ -3372,127 +3372,19 @@ log_args		:	log_arg
 log_arg			:	PREFIX			string
 			{
 				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);
-					free_const($2);
-					$<stmt>0->log.prefix = expr;
-					$<stmt>0->log.flags |= STMT_LOG_PREFIX;
-					break;
-				}
-
-				/* 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;
+				struct error_record *erec;
+				const char *prefix;
 
-				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);
-							free_const($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;
-					}
+				prefix = str_preprocess(state, &@2, scope, $2, &erec);
+				if (!prefix) {
+					erec_queue(erec, state->msgs);
+					free_const($2);
+					YYERROR;
 				}
 
 				free_const($2);
-				$<stmt>0->log.prefix	 = expr;
-				$<stmt>0->log.flags 	|= STMT_LOG_PREFIX;
+				$<stmt>0->log.prefix = prefix;
+				$<stmt>0->log.flags |= STMT_LOG_PREFIX;
 			}
 			|	GROUP			NUM
 			{
diff --git a/src/parser_json.c b/src/parser_json.c
index 8b7efaf22662..ee4657ee8044 100644
--- a/src/parser_json.c
+++ b/src/parser_json.c
@@ -2565,9 +2565,7 @@ 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 = constant_expr_alloc(int_loc, &string_type,
-						       BYTEORDER_HOST_ENDIAN,
-						       (strlen(tmpstr) + 1) * BITS_PER_BYTE, tmpstr);
+		stmt->log.prefix = xstrdup(tmpstr);
 		stmt->log.flags |= STMT_LOG_PREFIX;
 	}
 	if (!json_unpack(value, "{s:i}", "group", &tmp)) {
diff --git a/src/preprocess.c b/src/preprocess.c
new file mode 100644
index 000000000000..619f67a15049
--- /dev/null
+++ b/src/preprocess.c
@@ -0,0 +1,168 @@
+/*
+ * Copyright (c) 2013-2024 Pablo Neira Ayuso <pablo@xxxxxxxxxxxxx>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 (or any
+ * later) as published by the Free Software Foundation.
+ */
+
+#include <ctype.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <string.h>
+#include <utils.h>
+
+#include "list.h"
+#include "parser.h"
+#include "erec.h"
+
+struct str_buf {
+	uint8_t		*str;
+	uint32_t	len;
+	uint32_t	size;
+};
+
+#define STR_BUF_LEN	128
+
+static struct str_buf *str_buf_alloc(void)
+{
+	struct str_buf *buf;
+
+	buf = xzalloc(sizeof(*buf));
+	buf->str = xzalloc_array(1, STR_BUF_LEN);
+	buf->size = STR_BUF_LEN;
+
+	return buf;
+}
+
+static int str_buf_add(struct str_buf *buf, const char *str, uint32_t len)
+{
+	uint8_t *tmp;
+
+	if (len + buf->len > buf->size) {
+		buf->size = (len + buf->len) * 2;
+		tmp = xrealloc(buf->str, buf->size);
+		buf->str = tmp;
+	}
+
+	memcpy(&buf->str[buf->len], str, len);
+	buf->len += len;
+
+	return 0;
+}
+
+struct str_chunk {
+	struct list_head	list;
+	char			*str;
+	uint32_t		len;
+	bool			is_sym;
+};
+
+static void add_str_chunk(const char *x, int from, int to, struct list_head *list, bool is_sym)
+{
+	struct str_chunk *chunk;
+	int len = to - from;
+
+	chunk = xzalloc_array(1, sizeof(*chunk));
+	chunk->str = xzalloc_array(1, len + 1);
+	chunk->is_sym = is_sym;
+	chunk->len = len;
+	memcpy(chunk->str, &x[from], len);
+
+	list_add_tail(&chunk->list, list);
+}
+
+static void free_str_chunk(struct str_chunk *chunk)
+{
+	free(chunk->str);
+	free(chunk);
+}
+
+const char *str_preprocess(struct parser_state *state, struct location *loc,
+			   struct scope *scope, const char *x,
+			   struct error_record **erec)
+{
+	struct str_chunk *chunk, *next;
+	struct str_buf *buf;
+	const char *str;
+	int i, j, start;
+	LIST_HEAD(list);
+
+	start = 0;
+	i = 0;
+	while (1) {
+		if (x[i] == '\0') {
+			i++;
+			break;
+		}
+
+		if (x[i] != '$') {
+			i++;
+			continue;
+		}
+
+		if (isdigit(x[++i]))
+			continue;
+
+		j = i;
+		while (1) {
+			if (isalpha(x[i]) ||
+			    isdigit(x[i]) ||
+			    x[i] == '_') {
+				i++;
+				continue;
+			}
+			break;
+		}
+		add_str_chunk(x, start, j-1, &list, false);
+		add_str_chunk(x, j, i, &list, true);
+		start = i;
+	}
+	if (start != i)
+		add_str_chunk(x, start, i, &list, false);
+
+	buf = str_buf_alloc();
+
+	list_for_each_entry_safe(chunk, next, &list, list) {
+		if (chunk->is_sym) {
+			struct symbol *sym;
+
+			sym = symbol_lookup(scope, chunk->str);
+			if (!sym) {
+				sym = symbol_lookup_fuzzy(scope, chunk->str);
+				if (sym) {
+					*erec = error(loc, "unknown identifier '%s'; "
+							   "did you mean identifier '%s'?",
+					              chunk->str, sym->identifier);
+				} else {
+					*erec = error(loc, "unknown identifier '%s'",
+						      chunk->str);
+				}
+				goto err;
+			}
+			str_buf_add(buf, sym->expr->identifier,
+				    strlen(sym->expr->identifier));
+		} else {
+			str_buf_add(buf, chunk->str, chunk->len);
+		}
+		list_del(&chunk->list);
+		free_str_chunk(chunk);
+	}
+
+	str = (char *)buf->str;
+
+	free(buf);
+
+	return (char *)str;
+err:
+	list_for_each_entry_safe(chunk, next, &list, list) {
+		list_del(&chunk->list);
+		free_str_chunk(chunk);
+	}
+	free(buf->str);
+	free(buf);
+
+	return NULL;
+}
diff --git a/src/statement.c b/src/statement.c
index ab144d639318..551cd13fa04e 100644
--- a/src/statement.c
+++ b/src/statement.c
@@ -377,12 +377,8 @@ 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) {
-		char prefix[NF_LOG_PREFIXLEN] = {};
-
-		expr_to_string(stmt->log.prefix, prefix);
-		nft_print(octx, " prefix \"%s\"", prefix);
-	}
+	if (stmt->log.flags & STMT_LOG_PREFIX)
+		nft_print(octx, " prefix \"%s\"", stmt->log.prefix);
 	if (stmt->log.flags & STMT_LOG_GROUP)
 		nft_print(octx, " group %u", stmt->log.group);
 	if (stmt->log.flags & STMT_LOG_SNAPLEN)
@@ -419,7 +415,7 @@ static void log_stmt_print(const struct stmt *stmt, struct output_ctx *octx)
 
 static void log_stmt_destroy(struct stmt *stmt)
 {
-	expr_free(stmt->log.prefix);
+	free_const(stmt->log.prefix);
 }
 
 static const struct stmt_ops log_stmt_ops = {
-- 
2.30.2





[Index of Archives]     [Netfitler Users]     [Berkeley Packet Filter]     [LARTC]     [Bugtraq]     [Yosemite Forum]

  Powered by Linux