diff mbox

[nft,v2] src: add level option to the log statement

Message ID 1406305207-21853-1-git-send-email-pablo@netfilter.org
State Accepted
Delegated to: Pablo Neira
Headers show

Commit Message

Pablo Neira Ayuso July 25, 2014, 4:20 p.m. UTC
This patch is required if you use upcoming Linux kernels >= 3.17
which come with a complete logging support for nf_tables.

If you use 'log' without options, the kernel logging buffer is used:

nft> add rule filter input log

You can also specify the logging prefix string:

nft> add rule filter input log prefix "input: "

You may want to specify the log level:

nft> add rule filter input log prefix "input: " level notice

By default, if not specified, the default level is 'warn' (just like
in iptables).

If you specify the group, then nft uses the nfnetlink_log instead:

nft> add rule filter input log prefix "input: " group 10

You can also specify the snaplen and qthreshold for the nfnetlink_log.
But you cannot mix level and group at the same time, they are mutually
exclusive.

Default values for both snaplen and qthreshold are 0 (just like in
iptables).

Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
---
v2: use the scanner to identify the log level instead of strcmp.
    Suggested by Patrick.

 include/statement.h       |   10 +++++++++
 src/evaluate.c            |   15 ++++++++++++-
 src/netlink_delinearize.c |   28 ++++++++++++++++++-----
 src/netlink_linearize.c   |   18 +++++++--------
 src/parser.y              |   54 +++++++++++++++++++++++++++++++++++++++++++++
 src/scanner.l             |    9 ++++++++
 src/statement.c           |   31 ++++++++++++++++++++++----
 7 files changed, 146 insertions(+), 19 deletions(-)
diff mbox

Patch

diff --git a/include/statement.h b/include/statement.h
index 480b719..12336bc 100644
--- a/include/statement.h
+++ b/include/statement.h
@@ -28,11 +28,21 @@  extern struct stmt *meta_stmt_alloc(const struct location *loc,
 				    enum nft_meta_keys key,
 				    struct expr *expr);
 
+enum {
+	STMT_LOG_PREFIX		= (1 << 0),
+	STMT_LOG_SNAPLEN	= (1 << 1),
+	STMT_LOG_GROUP		= (1 << 2),
+	STMT_LOG_QTHRESHOLD	= (1 << 3),
+	STMT_LOG_LEVEL		= (1 << 4),
+};
+
 struct log_stmt {
 	const char		*prefix;
 	unsigned int		snaplen;
 	uint16_t		group;
 	uint16_t		qthreshold;
+	uint32_t		level;
+	uint32_t		flags;
 };
 
 extern struct stmt *log_stmt_alloc(const struct location *loc);
diff --git a/src/evaluate.c b/src/evaluate.c
index e05473a..f66a8ea 100644
--- a/src/evaluate.c
+++ b/src/evaluate.c
@@ -1180,6 +1180,18 @@  static int stmt_evaluate_ct(struct eval_ctx *ctx, struct stmt *stmt)
 	return 0;
 }
 
+static int stmt_evaluate_log(struct eval_ctx *ctx, struct stmt *stmt)
+{
+	if (stmt->log.flags & STMT_LOG_LEVEL &&
+	    (stmt->log.flags & STMT_LOG_GROUP	||
+	     stmt->log.flags & STMT_LOG_SNAPLEN	||
+	     stmt->log.flags & STMT_LOG_QTHRESHOLD)) {
+		return stmt_error(ctx, stmt,
+				  "level and group are mutually exclusive");
+	}
+	return 0;
+}
+
 static int stmt_evaluate(struct eval_ctx *ctx, struct stmt *stmt)
 {
 #ifdef DEBUG
@@ -1193,7 +1205,6 @@  static int stmt_evaluate(struct eval_ctx *ctx, struct stmt *stmt)
 	switch (stmt->ops->type) {
 	case STMT_COUNTER:
 	case STMT_LIMIT:
-	case STMT_LOG:
 		return 0;
 	case STMT_EXPRESSION:
 		return stmt_evaluate_expr(ctx, stmt);
@@ -1201,6 +1212,8 @@  static int stmt_evaluate(struct eval_ctx *ctx, struct stmt *stmt)
 		return stmt_evaluate_verdict(ctx, stmt);
 	case STMT_META:
 		return stmt_evaluate_meta(ctx, stmt);
+	case STMT_LOG:
+		return stmt_evaluate_log(ctx, stmt);
 	case STMT_REJECT:
 		return stmt_evaluate_reject(ctx, stmt);
 	case STMT_NAT:
diff --git a/src/netlink_delinearize.c b/src/netlink_delinearize.c
index 5c6ca80..195d432 100644
--- a/src/netlink_delinearize.c
+++ b/src/netlink_delinearize.c
@@ -428,12 +428,30 @@  static void netlink_parse_log(struct netlink_parse_ctx *ctx,
 
 	stmt = log_stmt_alloc(loc);
 	prefix = nft_rule_expr_get_str(nle, NFT_EXPR_LOG_PREFIX);
-	if (prefix != NULL)
+	if (nft_rule_expr_is_set(nle, NFT_EXPR_LOG_PREFIX)) {
 		stmt->log.prefix = xstrdup(prefix);
-	stmt->log.group = nft_rule_expr_get_u16(nle, NFT_EXPR_LOG_GROUP);
-	stmt->log.snaplen = nft_rule_expr_get_u32(nle, NFT_EXPR_LOG_SNAPLEN);
-	stmt->log.qthreshold =
-		nft_rule_expr_get_u16(nle, NFT_EXPR_LOG_QTHRESHOLD);
+		stmt->log.flags |= STMT_LOG_PREFIX;
+	}
+	if (nft_rule_expr_is_set(nle, NFT_EXPR_LOG_GROUP)) {
+		stmt->log.group =
+			nft_rule_expr_get_u16(nle, NFT_EXPR_LOG_GROUP);
+		stmt->log.flags |= STMT_LOG_GROUP;
+	}
+	if (nft_rule_expr_is_set(nle, NFT_EXPR_LOG_SNAPLEN)) {
+		stmt->log.snaplen =
+			nft_rule_expr_get_u32(nle, NFT_EXPR_LOG_SNAPLEN);
+		stmt->log.flags |= STMT_LOG_SNAPLEN;
+	}
+	if (nft_rule_expr_is_set(nle, NFT_EXPR_LOG_QTHRESHOLD)) {
+		stmt->log.qthreshold =
+			nft_rule_expr_get_u16(nle, NFT_EXPR_LOG_QTHRESHOLD);
+		stmt->log.flags |= STMT_LOG_QTHRESHOLD;
+	}
+	if (nft_rule_expr_is_set(nle, NFT_EXPR_LOG_LEVEL)) {
+		stmt->log.level =
+			nft_rule_expr_get_u32(nle, NFT_EXPR_LOG_LEVEL);
+		stmt->log.flags |= STMT_LOG_LEVEL;
+	}
 	list_add_tail(&stmt->list, &ctx->rule->stmts);
 }
 
diff --git a/src/netlink_linearize.c b/src/netlink_linearize.c
index 5c1b46d..075e243 100644
--- a/src/netlink_linearize.c
+++ b/src/netlink_linearize.c
@@ -576,17 +576,17 @@  static void netlink_gen_log_stmt(struct netlink_linearize_ctx *ctx,
 		nft_rule_expr_set_str(nle, NFT_EXPR_LOG_PREFIX,
 				      stmt->log.prefix);
 	}
-	if (stmt->log.group) {
+	if (stmt->log.flags & STMT_LOG_GROUP) {
 		nft_rule_expr_set_u16(nle, NFT_EXPR_LOG_GROUP,
 				      stmt->log.group);
-	}
-	if (stmt->log.snaplen) {
-		nft_rule_expr_set_u32(nle, NFT_EXPR_LOG_SNAPLEN,
-				      stmt->log.snaplen);
-	}
-	if (stmt->log.qthreshold) {
-		nft_rule_expr_set_u16(nle, NFT_EXPR_LOG_QTHRESHOLD,
-				      stmt->log.qthreshold);
+		if (stmt->log.flags & STMT_LOG_SNAPLEN)
+			nft_rule_expr_set_u32(nle, NFT_EXPR_LOG_SNAPLEN,
+					      stmt->log.snaplen);
+		if (stmt->log.flags & STMT_LOG_QTHRESHOLD)
+			nft_rule_expr_set_u16(nle, NFT_EXPR_LOG_QTHRESHOLD,
+					      stmt->log.qthreshold);
+	} else {
+		nft_rule_expr_set_u32(nle, NFT_EXPR_LOG_LEVEL, stmt->log.level);
 	}
 	nft_rule_add_expr(ctx->nlr, nle);
 }
diff --git a/src/parser.y b/src/parser.y
index 3e08e21..26d2879 100644
--- a/src/parser.y
+++ b/src/parser.y
@@ -13,6 +13,7 @@ 
 #include <stddef.h>
 #include <stdio.h>
 #include <inttypes.h>
+#include <syslog.h>
 #include <netinet/ip.h>
 #include <netinet/if_ether.h>
 #include <linux/netfilter.h>
@@ -345,6 +346,15 @@  static int monitor_lookup_event(const char *event)
 %token GROUP			"group"
 %token SNAPLEN			"snaplen"
 %token QUEUE_THRESHOLD		"queue-threshold"
+%token LEVEL			"level"
+%token LEVEL_EMERG		"emerg"
+%token LEVEL_ALERT		"alert"
+%token LEVEL_CRIT		"crit"
+%token LEVEL_ERR		"err"
+%token LEVEL_WARN		"warn"
+%token LEVEL_NOTICE		"notice"
+%token LEVEL_INFO		"info"
+%token LEVEL_DEBUG		"debug"
 
 %token LIMIT			"limit"
 %token RATE			"rate"
@@ -416,6 +426,7 @@  static int monitor_lookup_event(const char *event)
 %destructor { stmt_free($$); }	meta_stmt
 %type <stmt>			log_stmt log_stmt_alloc
 %destructor { stmt_free($$); }	log_stmt log_stmt_alloc
+%type <val>			level_type
 %type <stmt>			limit_stmt
 %destructor { stmt_free($$); }	limit_stmt
 %type <val>			time_unit
@@ -1366,18 +1377,61 @@  log_args		:	log_arg
 log_arg			:	PREFIX			string
 			{
 				$<stmt>0->log.prefix	 = $2;
+				$<stmt>0->log.flags 	|= STMT_LOG_PREFIX;
 			}
 			|	GROUP			NUM
 			{
 				$<stmt>0->log.group	 = $2;
+				$<stmt>0->log.flags 	|= STMT_LOG_GROUP;
 			}
 			|	SNAPLEN			NUM
 			{
 				$<stmt>0->log.snaplen	 = $2;
+				$<stmt>0->log.flags 	|= STMT_LOG_SNAPLEN;
 			}
 			|	QUEUE_THRESHOLD		NUM
 			{
 				$<stmt>0->log.qthreshold = $2;
+				$<stmt>0->log.flags 	|= STMT_LOG_QTHRESHOLD;
+			}
+			|	LEVEL			level_type
+			{
+				$<stmt>0->log.level	= $2;
+				$<stmt>0->log.flags 	|= STMT_LOG_LEVEL;
+			}
+			;
+
+level_type		:	LEVEL_EMERG
+			{
+				$$ = LOG_EMERG;
+			}
+			|	LEVEL_ALERT
+			{
+				$$ = LOG_ALERT;
+			}
+			|	LEVEL_CRIT
+			{
+				$$ = LOG_CRIT;
+			}
+			|	LEVEL_ERR
+			{
+				$$ = LOG_ERR;
+			}
+			|	LEVEL_WARN
+			{
+				$$ = LOG_WARNING;
+			}
+			|	LEVEL_NOTICE
+			{
+				$$ = LOG_NOTICE;
+			}
+			|	LEVEL_INFO
+			{
+				$$ = LOG_INFO;
+			}
+			|	LEVEL_DEBUG
+			{
+				$$ = LOG_DEBUG;
 			}
 			;
 
diff --git a/src/scanner.l b/src/scanner.l
index 73a1a3f..4eec92f 100644
--- a/src/scanner.l
+++ b/src/scanner.l
@@ -276,6 +276,15 @@  addrstring	({macaddr}|{ip4addr}|{ip6addr})
 "group"			{ return GROUP; }
 "snaplen"		{ return SNAPLEN; }
 "queue-threshold"	{ return QUEUE_THRESHOLD; }
+"level"			{ return LEVEL; }
+"emerg"			{ return LEVEL_EMERG; }
+"alert"			{ return LEVEL_ALERT; }
+"crit"			{ return LEVEL_CRIT; }
+"err"			{ return LEVEL_ERR; }
+"warn"			{ return LEVEL_WARN; }
+"notice"		{ return LEVEL_NOTICE; }
+"info"			{ return LEVEL_INFO; }
+"debug"			{ return LEVEL_DEBUG; }
 
 "queue"			{ return QUEUE;}
 "num"			{ return QUEUENUM;}
diff --git a/src/statement.c b/src/statement.c
index 2dd3f18..4be6625 100644
--- a/src/statement.c
+++ b/src/statement.c
@@ -14,6 +14,7 @@ 
 #include <stdint.h>
 #include <inttypes.h>
 #include <string.h>
+#include <syslog.h>
 
 #include <statement.h>
 #include <utils.h>
@@ -112,17 +113,39 @@  struct stmt *counter_stmt_alloc(const struct location *loc)
 	return stmt_alloc(loc, &counter_stmt_ops);
 }
 
+static const char *syslog_level[LOG_DEBUG + 1] = {
+	[LOG_EMERG]	= "emerg",
+	[LOG_ALERT]	= "alert",
+	[LOG_CRIT]	= "crit",
+	[LOG_ERR]       = "err",
+	[LOG_WARNING]	= "warn",
+	[LOG_NOTICE]	= "notice",
+	[LOG_INFO]	= "info",
+	[LOG_DEBUG]	= "debug",
+};
+
+static const char *log_level(uint32_t level)
+{
+	if (level > LOG_DEBUG)
+		return "unknown";
+
+	return syslog_level[level];
+}
+
 static void log_stmt_print(const struct stmt *stmt)
 {
 	printf("log");
-	if (stmt->log.prefix != NULL)
+	if (stmt->log.flags & STMT_LOG_PREFIX)
 		printf(" prefix \"%s\"", stmt->log.prefix);
-	if (stmt->log.group)
+	if (stmt->log.flags & STMT_LOG_GROUP)
 		printf(" group %u", stmt->log.group);
-	if (stmt->log.snaplen)
+	if (stmt->log.flags & STMT_LOG_SNAPLEN)
 		printf(" snaplen %u", stmt->log.snaplen);
-	if (stmt->log.qthreshold)
+	if (stmt->log.flags & STMT_LOG_QTHRESHOLD)
 		printf(" queue-threshold %u", stmt->log.qthreshold);
+	if ((stmt->log.flags & STMT_LOG_LEVEL) &&
+	    stmt->log.level != LOG_WARNING)
+		printf(" level %s", log_level(stmt->log.level));
 }
 
 static void log_stmt_destroy(struct stmt *stmt)