nftables/SOURCES/0100-src-add-string-preprocessor-and-use-it-for-log-prefi.patch
2026-08-26 08:03:54 -04:00

608 lines
17 KiB
Diff
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

From cf8cedf79d49736dad61f01628a0cf738fefad26 Mon Sep 17 00:00:00 2001
From: Phil Sutter <psutter@redhat.com>
Date: Fri, 17 Jul 2026 11:09:42 +0200
Subject: [PATCH] src: add string preprocessor and use it for log prefix string
JIRA: https://issues.redhat.com/browse/RHEL-190549
Upstream Status: nftables commit 081bf5f0d7952a6e6ac0d23a365ccf1fd27010c0
Conflicts: Manually applied Makefile.am change due to missing commit
11e62138424ad ("build: no recursive make for "src/Makefile.am"")
commit 081bf5f0d7952a6e6ac0d23a365ccf1fd27010c0
Author: Pablo Neira Ayuso <pablo@netfilter.org>
Date: Tue Jun 18 14:26:31 2024 +0200
src: add string preprocessor and use it for log prefix string
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@netfilter.org>
Signed-off-by: Phil Sutter <psutter@redhat.com>
---
include/expression.h | 2 -
include/parser.h | 4 +
include/statement.h | 2 +-
src/Makefile.am | 1 +
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/include/expression.h b/include/expression.h
index 809089c..e143e71 100644
--- a/include/expression.h
+++ b/include/expression.h
@@ -414,8 +414,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 f79a22f..576e5e4 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 662f99d..9376911 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/Makefile.am b/src/Makefile.am
index 63a4ef4..6d7bb89 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -75,6 +75,7 @@ libnftables_la_SOURCES = \
nfnl_osf.c \
tcpopt.c \
socket.c \
+ preprocess.c \
print.c \
sctp_chunk.c \
dccpopt.c \
diff --git a/src/evaluate.c b/src/evaluate.c
index 7f5b64b..c28f693 100644
--- a/src/evaluate.c
+++ b/src/evaluate.c
@@ -4292,49 +4292,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 cb2573f..992f510 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 b3e1e4e..5faeb4d 100644
--- a/src/json.c
+++ b/src/json.c
@@ -1319,12 +1319,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 1af0278..0ea10ee 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 df395ba..a2a1cd5 100644
--- a/src/netlink_linearize.c
+++ b/src/netlink_linearize.c
@@ -1141,12 +1141,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 b90dd99..1dd0858 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 e3ac2fb..678dd50 100644
--- a/src/parser_bison.y
+++ b/src/parser_bison.y
@@ -3338,127 +3338,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 25483b1..ce36397 100644
--- a/src/parser_json.c
+++ b/src/parser_json.c
@@ -2540,9 +2540,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 0000000..619f67a
--- /dev/null
+++ b/src/preprocess.c
@@ -0,0 +1,168 @@
+/*
+ * Copyright (c) 2013-2024 Pablo Neira Ayuso <pablo@netfilter.org>
+ *
+ * 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 ab144d6..551cd13 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 = {