From ed6db59e68729a968a2152a8460b022d46d0ab13 Mon Sep 17 00:00:00 2001 From: Joachim Wiberg Date: Mon, 17 Nov 2025 14:19:19 +0100 Subject: [PATCH] confd: extend syslogd support with property-based filtering This is the last commit in the series that extend syslog matching and sorting to the level of sysklogd 2.7.0 and later. Like hostname filtering, property based filtering is not supported natively in the IETF RFC, and the modeling is unfortunately a bit clunky. The most confusing part is probably 'negate' which is the '!' operator that inverts the matching, e.g., !icase_regex match everthing *not* in the regexp. Fixes #1091 Signed-off-by: Joachim Wiberg --- src/confd/src/ietf-syslog.c | 34 ++++++++++++ src/confd/yang/confd/infix-syslog.yang | 77 ++++++++++++++++++++++++++ 2 files changed, 111 insertions(+) diff --git a/src/confd/src/ietf-syslog.c b/src/confd/src/ietf-syslog.c index d3c3d320..09d52f70 100644 --- a/src/confd/src/ietf-syslog.c +++ b/src/confd/src/ietf-syslog.c @@ -118,6 +118,40 @@ static size_t selector(sr_session_ctx_t *session, struct action *act) count = 0; } + /* Check for property-filter (infix-syslog augment) */ + char *property = srx_get_str(session, "%s/infix-syslog:property-filter/property", act->xpath); + if (property) { + char *operator = srx_get_str(session, "%s/infix-syslog:property-filter/operator", act->xpath); + char *value = srx_get_str(session, "%s/infix-syslog:property-filter/value", act->xpath); + char *case_str = srx_get_str(session, "%s/infix-syslog:property-filter/case-insensitive", act->xpath); + char *negate_str = srx_get_str(session, "%s/infix-syslog:property-filter/negate", act->xpath); + + if (operator && value) { + /* Build operator prefix: [!][icase_]operator */ + char op_prefix[32] = ""; + + /* Only apply negate if explicitly set to true */ + if (negate_str && !strcmp(negate_str, "true")) + strlcat(op_prefix, "!", sizeof(op_prefix)); + + /* Only apply icase_ if explicitly set to true */ + if (case_str && !strcmp(case_str, "true")) + strlcat(op_prefix, "icase_", sizeof(op_prefix)); + + strlcat(op_prefix, operator, sizeof(op_prefix)); + + /* Property-based filter: :property, [!][icase_]operator, "value" */ + fprintf(act->fp, ":%s, %s, \"%s\"\n", property, op_prefix, value); + has_pattern = true; + } + + free(property); + free(operator); + free(value); + free(case_str); + free(negate_str); + } + /* Check for pattern-match (select-match feature) */ pattern = srx_get_str(session, "%s/pattern-match", act->xpath); if (pattern) { diff --git a/src/confd/yang/confd/infix-syslog.yang b/src/confd/yang/confd/infix-syslog.yang index 72d5124f..981f498d 100644 --- a/src/confd/yang/confd/infix-syslog.yang +++ b/src/confd/yang/confd/infix-syslog.yang @@ -175,6 +175,83 @@ module infix-syslog { description "Filter messages by hostname. Messages from the listed hostnames will be logged to this file."; } + + container property-filter { + presence "Enable property-based filtering"; + description "Advanced property-based message filtering. Allows filtering + on specific message fields with various comparison operators."; + + leaf property { + type enumeration { + enum msg { + description "The message body (SYSLOG-MSG field)."; + } + enum msgid { + description "The RFC5424 message identifier (MSGID field)."; + } + enum programname { + description "The originating program or tag name."; + } + enum hostname { + description "The message source hostname."; + } + enum source { + description "The message source (alias for hostname)."; + } + enum data { + description "The RFC5424 structured data (SD field)."; + } + } + mandatory true; + description "The message property to match against."; + } + + leaf operator { + type enumeration { + enum contains { + description "Substring match (case-sensitive)."; + } + enum isequal { + description "Exact equality match."; + } + enum startswith { + description "Prefix match."; + } + enum regex { + description "Basic regular expression match."; + } + enum ereregex { + description "Extended regular expression match (POSIX ERE)."; + } + } + mandatory true; + description "The comparison operator to use."; + } + + leaf value { + type string; + mandatory true; + description "The value to compare against."; + } + + leaf case-insensitive { + type boolean; + description "Perform case-insensitive comparison."; + } + + leaf negate { + type boolean; + description "Negate the comparison result. When set to true, the + filter matches messages that do NOT satisfy the + specified condition. + + For example: + - operator: contains, value: 'ERROR', negate: false + → matches messages containing 'ERROR' + - operator: contains, value: 'ERROR', negate: true + → matches messages NOT containing 'ERROR'"; + } + } } augment "/syslog:syslog/syslog:actions/syslog:remote/syslog:destination" {